From 63eb4b02c537c986046ea1a0e9a686ca5de8aad5 Mon Sep 17 00:00:00 2001 From: meisnate12 Date: Thu, 20 Jan 2022 16:20:49 -0500 Subject: [PATCH] Initial Release --- .github/FUNDING.yml | 1 + .github/ISSUE_TEMPLATE/bug_report.md | 15 + .github/ISSUE_TEMPLATE/config.yml | 8 + .github/ISSUE_TEMPLATE/feature_request.md | 20 + .github/pull_request_template.md | 21 + .github/workflows/release.yml | 23 + .github/workflows/tag.yml | 18 + .gitignore | 130 + .readthedocs.yml | 17 + .travis.yml | 13 + LICENSE | 21 + README.rst | 195 ++ VERSION | 1 + docs/Makefile | 20 + docs/conf.py | 82 + docs/exceptions.rst | 7 + docs/index.rst | 5 + docs/intro.rst | 195 ++ docs/make.bat | 35 + docs/objapi.rst | 7 + docs/objs/image.rst | 38 + docs/objs/pagination.rst | 194 ++ docs/objs/reload.rst | 81 + docs/objs/simple.rst | 96 + docs/rawapi.rst | 14 + docs/toc.rst | 24 + requirements.txt | 1 + setup.py | 49 + tests/test_api.py | 161 ++ tmdbapis/__init__.py | 71 + tmdbapis/api3.py | 2884 +++++++++++++++++++++ tmdbapis/api4.py | 449 ++++ tmdbapis/exceptions.py | 33 + tmdbapis/objs/__init__.py | 0 tmdbapis/objs/base.py | 269 ++ tmdbapis/objs/image.py | 97 + tmdbapis/objs/mixin.py | 59 + tmdbapis/objs/pagination.py | 984 +++++++ tmdbapis/objs/reload.py | 977 +++++++ tmdbapis/objs/simple.py | 397 +++ tmdbapis/tmdb.py | 1281 +++++++++ tmdbapis/util.py | 146 ++ 42 files changed, 9139 insertions(+) create mode 100644 .github/FUNDING.yml create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md create mode 100644 .github/ISSUE_TEMPLATE/config.yml create mode 100644 .github/ISSUE_TEMPLATE/feature_request.md create mode 100644 .github/pull_request_template.md create mode 100644 .github/workflows/release.yml create mode 100644 .github/workflows/tag.yml create mode 100644 .gitignore create mode 100644 .readthedocs.yml create mode 100644 .travis.yml create mode 100644 LICENSE create mode 100644 README.rst create mode 100644 VERSION create mode 100644 docs/Makefile create mode 100644 docs/conf.py create mode 100644 docs/exceptions.rst create mode 100644 docs/index.rst create mode 100644 docs/intro.rst create mode 100644 docs/make.bat create mode 100644 docs/objapi.rst create mode 100644 docs/objs/image.rst create mode 100644 docs/objs/pagination.rst create mode 100644 docs/objs/reload.rst create mode 100644 docs/objs/simple.rst create mode 100644 docs/rawapi.rst create mode 100644 docs/toc.rst create mode 100644 requirements.txt create mode 100644 setup.py create mode 100644 tests/test_api.py create mode 100644 tmdbapis/__init__.py create mode 100644 tmdbapis/api3.py create mode 100644 tmdbapis/api4.py create mode 100644 tmdbapis/exceptions.py create mode 100644 tmdbapis/objs/__init__.py create mode 100644 tmdbapis/objs/base.py create mode 100644 tmdbapis/objs/image.py create mode 100644 tmdbapis/objs/mixin.py create mode 100644 tmdbapis/objs/pagination.py create mode 100644 tmdbapis/objs/reload.py create mode 100644 tmdbapis/objs/simple.py create mode 100644 tmdbapis/tmdb.py create mode 100644 tmdbapis/util.py diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..d61e389 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +github: meisnate12 \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..a851e51 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,15 @@ +--- +name: Bug Report +about: Please do not use bug reports for support issues. +title: 'Bug: ' +labels: 'status:not-yet-viewed, bug' +assignees: 'meisnate12' + +--- + + + +**Describe the Bug** +A clear and concise description of what the bug is. diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..d4ca746 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,8 @@ +blank_issues_enabled: false +contact_links: + - name: Documentaion + url: https://tmdbapis.readthedocs.io/en/latest/ + about: Please check the Documentaion to see if your question has already been answered. + - name: Discussions + url: https://github.com/meisnate12/TMDbAPIs/discussions + about: Please use Discussions to ask for support. \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..79ad843 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,20 @@ +--- +name: Feature Request +about: Suggest a new feature for TMDbAPIs. +title: 'Feature Request: ' +labels: 'status:not-yet-viewed, enhancement' +assignees: 'meisnate12' + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000..b066b45 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,21 @@ +## Description + +Please include a summary of the changes. + +### Issues Fixed or Closed + +- Fixes #(issue) + +## Type of Change + +Please delete options that are not relevant. + +- [ ] Bug fix (non-breaking change which fixes an issue) +- [ ] New feature (non-breaking change which adds functionality) +- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) + +## Checklist + +- [ ] My code follows the style guidelines of this project +- [ ] I have performed a self-review of my own code +- [ ] I have commented my code, particularly in hard-to-understand areas diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..50bd245 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,23 @@ +name: release + +on: + create: + tags: + - v* + +jobs: + tagged-release: + name: Release + runs-on: ubuntu-latest + steps: + - name: Create Release for new Tag + uses: marvinpinto/action-automatic-releases@latest + with: + title: TMDbAPIs ${{ github.event.ref }} + repo_token: ${{ secrets.PAT }} + prerelease: false + - name: Publish a Python distribution to PyPI + uses: pypa/gh-action-pypi-publish@release/v1 + with: + user: __token__ + password: ${{ secrets.PYPI_API_TOKEN }} diff --git a/.github/workflows/tag.yml b/.github/workflows/tag.yml new file mode 100644 index 0000000..8a6aa3a --- /dev/null +++ b/.github/workflows/tag.yml @@ -0,0 +1,18 @@ +name: Tag + +on: + push: + branches: [ master ] + +jobs: + tag-new-versions: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + with: + token: ${{ secrets.PAT }} + fetch-depth: 2 + - uses: salsify/action-detect-and-tag-new-version@v1.0.3 + with: + version-command: | + cat VERSION diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0e75bdf --- /dev/null +++ b/.gitignore @@ -0,0 +1,130 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.idea +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +pip-wheel-metadata/ +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/ + +# 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 +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +.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 + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow +__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/ diff --git a/.readthedocs.yml b/.readthedocs.yml new file mode 100644 index 0000000..9fb5eb3 --- /dev/null +++ b/.readthedocs.yml @@ -0,0 +1,17 @@ +# .readthedocs.yml + +version: 2 + +build: + image: latest + +sphinx: + configuration: docs/conf.py + +formats: all + +python: + version: "3.7" + + system_packages: true + diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..7da3e47 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,13 @@ +language: python +python: +- "3.6" +- "3.7" +- "3.8" +- "3.9" +install: +- pip install -r requirements.txt +- pip install codecov +script: +- coverage run -m unittest discover -s tests/ +after_success: +- codecov diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..f7d1e59 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 meisnate12 + +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. diff --git a/README.rst b/README.rst new file mode 100644 index 0000000..fa83756 --- /dev/null +++ b/README.rst @@ -0,0 +1,195 @@ +Welcome to TMDbAPIs Documentation! +========================================================== + +.. image:: https://img.shields.io/readthedocs/tmdbapis?style=plastic + :target: https://tmdbapis.readthedocs.io/en/latest/?badge=latest + :alt: Read the Docs + +.. image:: https://img.shields.io/github/v/release/meisnate12/TMDbAPIs?style=plastic + :target: https://github.com/meisnate12/TMDbAPI/releases + :alt: GitHub release (latest by date) + +.. image:: https://img.shields.io/pypi/v/TMDbAPIs?style=plastic + :target: https://pypi.org/project/tmdbapis/ + :alt: PyPI + +.. image:: https://img.shields.io/pypi/dm/tmdbapis.svg?style=plastic + :target: https://pypi.org/project/tmdbapis/ + :alt: Downloads + +.. image:: https://img.shields.io/github/commits-since/meisnate12/TMDbAPIs/latest?style=plastic + :target: https://github.com/meisnate12/TMDbAPIs/commits/master + :alt: GitHub commits since latest release (by date) for a branch + +.. image:: https://img.shields.io/badge/-Sponsor_or_Donate-blueviolet?style=plastic + :target: https://github.com/sponsors/meisnate12 + :alt: GitHub Sponsor + + +Overview +---------------------------------------------------------- +Unofficial Python bindings for the TMDb API. The goal is to make interaction with the API as easy as possible while emulating the endpoints as much as possible + + +Installation & Documentation +---------------------------------------------------------- + +.. code-block:: python + + pip install tmdbapis + +Documentation_ can be found at Read the Docs. + +.. _Documentation: http://tmdbapis.readthedocs.io/en/latest/ + + +Using the Object API +========================================================== + + +Getting a TMDbAPIs Instance +---------------------------------------------------------- + +To create a TMDbAPIs Object you need your V3 API Key, which can be found following `this guide `_. + +.. code-block:: python + + from tmdbapis import TMDbAPIs + + apikey = "0010843563404748808d3fc9c562c05e" + + tmdb = TMDbAPIs(apikey) + + +Authenticating V3 API Token +---------------------------------------------------------- + +To authenticate your TMDb V3 API Token you can either authenticate your TMDb V4 Token or use the :meth:`~tmdbapis.tmdb.TMDbAPIs.authenticate` method. + +.. code-block:: python + + from tmdbapis import TMDbAPIs + + apikey = "0010843563404748808d3fc9c562c05e" + + tmdb = TMDbAPIs(apikey) + tmdb.authenticate(username, password) + + +Saving a V3 API Authenticated Session +---------------------------------------------------------- + +To save your authenticated session use the ``session_id`` Attribute. + +.. code-block:: python + + from tmdbapis import TMDbAPIs + + apikey = "0010843563404748808d3fc9c562c05e" + + tmdb = TMDbAPIs(apikey) + tmdb.authenticate(username, password) + with open("session_id.txt", "w") as text_file: + print(tmdb.session_id, file=text_file) + +To load the authenticated session use the ``session_id`` Parameter of the :class:`~tmdbapis.tmdb.TMDbAPIs` constructor. + +.. code-block:: python + + from tmdbapis import TMDbAPIs + + apikey = "0010843563404748808d3fc9c562c05e" + + session_id = None + with open("session_id.txt") as text_file: + session_id = text_file.readline() + + tmdb = TMDbAPIs(apikey, session_id=session_id) + + +Adding TMDb V4 API Read Access Token +---------------------------------------------------------- + +To gain read access to TMDb V4's API just provide you're TMDb V4 Access Token either using the ``v4_access_token`` Parameter of the :class:`~tmdbapis.tmdb.TMDbAPIs` constructor or by using the :meth:`~tmdbapis.tmdb.TMDbAPIs.v4_access_token` method. + +To gain read access to TMDb V4's API need your TMDb V4 Access Token, which can be found following `this guide `_. + +.. code-block:: python + + from tmdbapis import TMDbAPIs + + apikey = "0010843563404748808d3fc9c562c05e" + v4_access_token = "sohsnrfiemrsdvsavvt4h426GWEGW434gSgSdnjhcyuwbBYHBOSIYCBWgyNTYxNTY4OGQ5NTJjZCIsInN1YiI6IjVkMzM5ZmI0MmY4ZDAfdfdgegeGGregerfge34345BlcyI6WyJhcGlfcmVhZCJdLCJ2ZXJzaW9uIvfdvsdfveregrgqgfsfghjhOR0shmZZ_ZekFiuyl7o56921C0" + + tmdb = TMDbAPIs(apikey, v4_access_token=v4_access_token) + + +Authenticating TMDb V4 API Token +---------------------------------------------------------- + +To authenticate your TMDB V4 Read Access Token it is a multi step process. + + 1. Add your TMDb V4 API Read Access Token. + 2. Authenticate the URL returned from :meth:`~tmdbapis.tmdb.TMDbAPIs.v4_authenticate`. + 3. Once the URL has been authenticated you must approve it by running :meth:`~tmdbapis.tmdb.TMDbAPIs.v4_approved`. + +.. code-block:: python + + from tmdbapis import TMDbAPIs + + apikey = "0010843563404748808d3fc9c562c05e" + v4_access_token = "sohsnrfiemrsdvsavvt4h426GWEGW434gSgSdnjhcyuwbBYHBOSIYCBWgyNTYxNTY4OGQ5NTJjZCIsInN1YiI6IjVkMzM5ZmI0MmY4ZDAfdfdgegeGGregerfge34345BlcyI6WyJhcGlfcmVhZCJdLCJ2ZXJzaW9uIvfdvsdfveregrgqgfsfghjhOR0shmZZ_ZekFiuyl7o56921C0" + + tmdb = TMDbAPIs(apikey, v4_access_token=v4_access_token) + + print(tmdb.v4_authenticate()) + input("Navigate to the URL and then hit enter when Authenticated") + tmdb.v4_approved() + + +Saving a V4 API Authenticated Token +---------------------------------------------------------- + +To save your authenticated token use the ``v4_access_token`` Attribute. + +.. code-block:: python + + from tmdbapis import TMDbAPIs + + apikey = "0010843563404748808d3fc9c562c05e" + v4_access_token = "sohsnrfiemrsdvsavvt4h426GWEGW434gSgSdnjhcyuwbBYHBOSIYCBWgyNTYxNTY4OGQ5NTJjZCIsInN1YiI6IjVkMzM5ZmI0MmY4ZDAfdfdgegeGGregerfge34345BlcyI6WyJhcGlfcmVhZCJdLCJ2ZXJzaW9uIvfdvsdfveregrgqgfsfghjhOR0shmZZ_ZekFiuyl7o56921C0" + + tmdb = TMDbAPIs(apikey, v4_access_token=v4_access_token) + + print(tmdb.v4_authenticate()) + input("Navigate to the URL and then hit enter when Authenticated") + tmdb.v4_approved() + with open("access_token.txt", "w") as text_file: + print(tmdb.v4_access_token, file=text_file) + +To load the authenticated token use the ``v4_access_token`` Parameter of the :class:`~tmdbapis.tmdb.TMDbAPIs` constructor or the :meth:`~tmdbapis.tmdb.TMDbAPIs.v4_access_token` method. + +.. code-block:: python + + from tmdbapis import TMDbAPIs + + apikey = "0010843563404748808d3fc9c562c05e" + + v4_access_token = None + with open("access_token.txt") as text_file: + v4_access_token = text_file.readline() + + tmdb = TMDbAPIs(apikey, v4_access_token=v4_access_token) + + +Hyperlinks +---------------------------------------------------------- + +* `TMDb V3 API Docs `_ +* `TMDb V4 API Docs `_ + +Usage & Contributions +---------------------------------------------------------- +* Source is available on the `Github Project Page `_. +* Contributors to TMDbAPIs own their own contributions and may distribute that code under + the `MIT license `_. diff --git a/VERSION b/VERSION new file mode 100644 index 0000000..6da28dd --- /dev/null +++ b/VERSION @@ -0,0 +1 @@ +0.1.1 \ No newline at end of file diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 0000000..d4bb2cb --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,20 @@ +# Minimal makefile for Sphinx documentation +# + +# You can set these variables from the command line, and also +# from the environment for the first two. +SPHINXOPTS ?= +SPHINXBUILD ?= sphinx-build +SOURCEDIR = . +BUILDDIR = _build + +# Put it first so that "make" without argument is like "make help". +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +.PHONY: help Makefile + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/docs/conf.py b/docs/conf.py new file mode 100644 index 0000000..5c6701f --- /dev/null +++ b/docs/conf.py @@ -0,0 +1,82 @@ +# Configuration file for the Sphinx documentation builder. +# +# This file only contains a selection of the most common options. For a full +# list see the documentation: +# https://www.sphinx-doc.org/en/master/usage/configuration.html + +# -- Path setup -------------------------------------------------------------- + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +# +# import os +import sys, datetime +from os.path import abspath, dirname, join +path = dirname(dirname(abspath(__file__))) +sys.path.append(path) +sys.path.append(join(path, "tmdbapis")) +import tmdbapis + + +# -- Project information ----------------------------------------------------- + +project = tmdbapis.__project_name__ +author = tmdbapis.__author__ +copyright = f"{datetime.datetime.now().year}, {author}" + +# The full version, including alpha/beta/rc tags +release = tmdbapis.__version__ + + +# -- General configuration --------------------------------------------------- + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named "sphinx.ext.*") or your custom +# ones. +extensions = [ + 'sphinx.ext.autodoc', + 'sphinx.ext.coverage', + 'sphinx.ext.viewcode', + 'sphinx.ext.napoleon', + 'sphinx.ext.todo' +] + +# -- Napoleon Settings ----------------------------------------------------- +napoleon_google_docstring = True +napoleon_numpy_docstring = False +napoleon_include_init_with_doc = False +napoleon_include_private_with_doc = False +napoleon_include_special_with_doc = False +napoleon_use_admonition_for_examples = False +napoleon_use_admonition_for_notes = False +napoleon_use_admonition_for_references = False +napoleon_use_ivar = True +napoleon_use_param = True +napoleon_use_rtype = True +napoleon_use_keyword = True +autodoc_member_order = 'bysource' +add_module_names = False + +master_doc = 'index' + +# Add any paths that contain templates here, relative to this directory. +templates_path = ["_templates"] + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +# This pattern also affects html_static_path and html_extra_path. +exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"] + + +# -- Options for HTML output ------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +# +html_theme = "sphinx_rtd_theme" + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ["_static"] diff --git a/docs/exceptions.rst b/docs/exceptions.rst new file mode 100644 index 0000000..74f22d2 --- /dev/null +++ b/docs/exceptions.rst @@ -0,0 +1,7 @@ + +Exceptions +========================================================== + +.. automodule:: tmdbapis.exceptions + :members: + :show-inheritance: \ No newline at end of file diff --git a/docs/index.rst b/docs/index.rst new file mode 100644 index 0000000..d41d361 --- /dev/null +++ b/docs/index.rst @@ -0,0 +1,5 @@ +.. include:: intro.rst + +Table of Contents +---------------------------------------------------------- +.. include:: toc.rst \ No newline at end of file diff --git a/docs/intro.rst b/docs/intro.rst new file mode 100644 index 0000000..fa83756 --- /dev/null +++ b/docs/intro.rst @@ -0,0 +1,195 @@ +Welcome to TMDbAPIs Documentation! +========================================================== + +.. image:: https://img.shields.io/readthedocs/tmdbapis?style=plastic + :target: https://tmdbapis.readthedocs.io/en/latest/?badge=latest + :alt: Read the Docs + +.. image:: https://img.shields.io/github/v/release/meisnate12/TMDbAPIs?style=plastic + :target: https://github.com/meisnate12/TMDbAPI/releases + :alt: GitHub release (latest by date) + +.. image:: https://img.shields.io/pypi/v/TMDbAPIs?style=plastic + :target: https://pypi.org/project/tmdbapis/ + :alt: PyPI + +.. image:: https://img.shields.io/pypi/dm/tmdbapis.svg?style=plastic + :target: https://pypi.org/project/tmdbapis/ + :alt: Downloads + +.. image:: https://img.shields.io/github/commits-since/meisnate12/TMDbAPIs/latest?style=plastic + :target: https://github.com/meisnate12/TMDbAPIs/commits/master + :alt: GitHub commits since latest release (by date) for a branch + +.. image:: https://img.shields.io/badge/-Sponsor_or_Donate-blueviolet?style=plastic + :target: https://github.com/sponsors/meisnate12 + :alt: GitHub Sponsor + + +Overview +---------------------------------------------------------- +Unofficial Python bindings for the TMDb API. The goal is to make interaction with the API as easy as possible while emulating the endpoints as much as possible + + +Installation & Documentation +---------------------------------------------------------- + +.. code-block:: python + + pip install tmdbapis + +Documentation_ can be found at Read the Docs. + +.. _Documentation: http://tmdbapis.readthedocs.io/en/latest/ + + +Using the Object API +========================================================== + + +Getting a TMDbAPIs Instance +---------------------------------------------------------- + +To create a TMDbAPIs Object you need your V3 API Key, which can be found following `this guide `_. + +.. code-block:: python + + from tmdbapis import TMDbAPIs + + apikey = "0010843563404748808d3fc9c562c05e" + + tmdb = TMDbAPIs(apikey) + + +Authenticating V3 API Token +---------------------------------------------------------- + +To authenticate your TMDb V3 API Token you can either authenticate your TMDb V4 Token or use the :meth:`~tmdbapis.tmdb.TMDbAPIs.authenticate` method. + +.. code-block:: python + + from tmdbapis import TMDbAPIs + + apikey = "0010843563404748808d3fc9c562c05e" + + tmdb = TMDbAPIs(apikey) + tmdb.authenticate(username, password) + + +Saving a V3 API Authenticated Session +---------------------------------------------------------- + +To save your authenticated session use the ``session_id`` Attribute. + +.. code-block:: python + + from tmdbapis import TMDbAPIs + + apikey = "0010843563404748808d3fc9c562c05e" + + tmdb = TMDbAPIs(apikey) + tmdb.authenticate(username, password) + with open("session_id.txt", "w") as text_file: + print(tmdb.session_id, file=text_file) + +To load the authenticated session use the ``session_id`` Parameter of the :class:`~tmdbapis.tmdb.TMDbAPIs` constructor. + +.. code-block:: python + + from tmdbapis import TMDbAPIs + + apikey = "0010843563404748808d3fc9c562c05e" + + session_id = None + with open("session_id.txt") as text_file: + session_id = text_file.readline() + + tmdb = TMDbAPIs(apikey, session_id=session_id) + + +Adding TMDb V4 API Read Access Token +---------------------------------------------------------- + +To gain read access to TMDb V4's API just provide you're TMDb V4 Access Token either using the ``v4_access_token`` Parameter of the :class:`~tmdbapis.tmdb.TMDbAPIs` constructor or by using the :meth:`~tmdbapis.tmdb.TMDbAPIs.v4_access_token` method. + +To gain read access to TMDb V4's API need your TMDb V4 Access Token, which can be found following `this guide `_. + +.. code-block:: python + + from tmdbapis import TMDbAPIs + + apikey = "0010843563404748808d3fc9c562c05e" + v4_access_token = "sohsnrfiemrsdvsavvt4h426GWEGW434gSgSdnjhcyuwbBYHBOSIYCBWgyNTYxNTY4OGQ5NTJjZCIsInN1YiI6IjVkMzM5ZmI0MmY4ZDAfdfdgegeGGregerfge34345BlcyI6WyJhcGlfcmVhZCJdLCJ2ZXJzaW9uIvfdvsdfveregrgqgfsfghjhOR0shmZZ_ZekFiuyl7o56921C0" + + tmdb = TMDbAPIs(apikey, v4_access_token=v4_access_token) + + +Authenticating TMDb V4 API Token +---------------------------------------------------------- + +To authenticate your TMDB V4 Read Access Token it is a multi step process. + + 1. Add your TMDb V4 API Read Access Token. + 2. Authenticate the URL returned from :meth:`~tmdbapis.tmdb.TMDbAPIs.v4_authenticate`. + 3. Once the URL has been authenticated you must approve it by running :meth:`~tmdbapis.tmdb.TMDbAPIs.v4_approved`. + +.. code-block:: python + + from tmdbapis import TMDbAPIs + + apikey = "0010843563404748808d3fc9c562c05e" + v4_access_token = "sohsnrfiemrsdvsavvt4h426GWEGW434gSgSdnjhcyuwbBYHBOSIYCBWgyNTYxNTY4OGQ5NTJjZCIsInN1YiI6IjVkMzM5ZmI0MmY4ZDAfdfdgegeGGregerfge34345BlcyI6WyJhcGlfcmVhZCJdLCJ2ZXJzaW9uIvfdvsdfveregrgqgfsfghjhOR0shmZZ_ZekFiuyl7o56921C0" + + tmdb = TMDbAPIs(apikey, v4_access_token=v4_access_token) + + print(tmdb.v4_authenticate()) + input("Navigate to the URL and then hit enter when Authenticated") + tmdb.v4_approved() + + +Saving a V4 API Authenticated Token +---------------------------------------------------------- + +To save your authenticated token use the ``v4_access_token`` Attribute. + +.. code-block:: python + + from tmdbapis import TMDbAPIs + + apikey = "0010843563404748808d3fc9c562c05e" + v4_access_token = "sohsnrfiemrsdvsavvt4h426GWEGW434gSgSdnjhcyuwbBYHBOSIYCBWgyNTYxNTY4OGQ5NTJjZCIsInN1YiI6IjVkMzM5ZmI0MmY4ZDAfdfdgegeGGregerfge34345BlcyI6WyJhcGlfcmVhZCJdLCJ2ZXJzaW9uIvfdvsdfveregrgqgfsfghjhOR0shmZZ_ZekFiuyl7o56921C0" + + tmdb = TMDbAPIs(apikey, v4_access_token=v4_access_token) + + print(tmdb.v4_authenticate()) + input("Navigate to the URL and then hit enter when Authenticated") + tmdb.v4_approved() + with open("access_token.txt", "w") as text_file: + print(tmdb.v4_access_token, file=text_file) + +To load the authenticated token use the ``v4_access_token`` Parameter of the :class:`~tmdbapis.tmdb.TMDbAPIs` constructor or the :meth:`~tmdbapis.tmdb.TMDbAPIs.v4_access_token` method. + +.. code-block:: python + + from tmdbapis import TMDbAPIs + + apikey = "0010843563404748808d3fc9c562c05e" + + v4_access_token = None + with open("access_token.txt") as text_file: + v4_access_token = text_file.readline() + + tmdb = TMDbAPIs(apikey, v4_access_token=v4_access_token) + + +Hyperlinks +---------------------------------------------------------- + +* `TMDb V3 API Docs `_ +* `TMDb V4 API Docs `_ + +Usage & Contributions +---------------------------------------------------------- +* Source is available on the `Github Project Page `_. +* Contributors to TMDbAPIs own their own contributions and may distribute that code under + the `MIT license `_. diff --git a/docs/make.bat b/docs/make.bat new file mode 100644 index 0000000..922152e --- /dev/null +++ b/docs/make.bat @@ -0,0 +1,35 @@ +@ECHO OFF + +pushd %~dp0 + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set SOURCEDIR=. +set BUILDDIR=_build + +if "%1" == "" goto help + +%SPHINXBUILD% >NUL 2>NUL +if errorlevel 9009 ( + echo. + echo.The 'sphinx-build' command was not found. Make sure you have Sphinx + echo.installed, then set the SPHINXBUILD environment variable to point + echo.to the full path of the 'sphinx-build' executable. Alternatively you + echo.may add the Sphinx directory to PATH. + echo. + echo.If you don't have Sphinx installed, grab it from + echo.http://sphinx-doc.org/ + exit /b 1 +) + +%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% +goto end + +:help +%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% + +:end +popd diff --git a/docs/objapi.rst b/docs/objapi.rst new file mode 100644 index 0000000..e25df35 --- /dev/null +++ b/docs/objapi.rst @@ -0,0 +1,7 @@ + +Object API +========================================================== + +.. automodule:: tmdbapis.tmdb + :members: + :show-inheritance: diff --git a/docs/objs/image.rst b/docs/objs/image.rst new file mode 100644 index 0000000..0c05089 --- /dev/null +++ b/docs/objs/image.rst @@ -0,0 +1,38 @@ +************** +Image Objects +************** + + +.. autoclass:: tmdbapis.objs.image.TMDbImage + :members: + :show-inheritance: + + +.. autoclass:: tmdbapis.objs.image.Backdrop + :members: + :show-inheritance: + + +.. autoclass:: tmdbapis.objs.image.Logo + :members: + :show-inheritance: + + +.. autoclass:: tmdbapis.objs.image.Poster + :members: + :show-inheritance: + + +.. autoclass:: tmdbapis.objs.image.Profile + :members: + :show-inheritance: + + +.. autoclass:: tmdbapis.objs.image.Still + :members: + :show-inheritance: + + +.. autoclass:: tmdbapis.objs.image.Tagged + :members: + :show-inheritance: diff --git a/docs/objs/pagination.rst b/docs/objs/pagination.rst new file mode 100644 index 0000000..6629583 --- /dev/null +++ b/docs/objs/pagination.rst @@ -0,0 +1,194 @@ +************** +Pagination Objects +************** + + +.. autoclass:: tmdbapis.objs.pagination.TMDbPagination + :members: + :show-inheritance: + + +.. autoclass:: tmdbapis.objs.pagination.CreatedLists + :members: + :show-inheritance: + + +.. autoclass:: tmdbapis.objs.pagination.DiscoverMovies + :members: + :show-inheritance: + + +.. autoclass:: tmdbapis.objs.pagination.DiscoverTVShows + :members: + :show-inheritance: + + +.. autoclass:: tmdbapis.objs.pagination.FavoriteMovies + :members: + :show-inheritance: + + +.. autoclass:: tmdbapis.objs.pagination.FavoriteTVShows + :members: + :show-inheritance: + + +.. autoclass:: tmdbapis.objs.pagination.RatedEpisodes + :members: + :show-inheritance: + + +.. autoclass:: tmdbapis.objs.pagination.RatedMovies + :members: + :show-inheritance: + + +.. autoclass:: tmdbapis.objs.pagination.RatedTVShows + :members: + :show-inheritance: + + +.. autoclass:: tmdbapis.objs.pagination.MovieLists + :members: + :show-inheritance: + + +.. autoclass:: tmdbapis.objs.pagination.MovieRecommendations + :members: + :show-inheritance: + + +.. autoclass:: tmdbapis.objs.pagination.MovieReviews + :members: + :show-inheritance: + + +.. autoclass:: tmdbapis.objs.pagination.MovieWatchlist + :members: + :show-inheritance: + + +.. autoclass:: tmdbapis.objs.pagination.NowPlayingMovies + :members: + :show-inheritance: + + +.. autoclass:: tmdbapis.objs.pagination.PopularMovies + :members: + :show-inheritance: + + +.. autoclass:: tmdbapis.objs.pagination.PopularPeople + :members: + :show-inheritance: + + +.. autoclass:: tmdbapis.objs.pagination.PopularTVShows + :members: + :show-inheritance: + + +.. autoclass:: tmdbapis.objs.pagination.RecommendedMovies + :members: + :show-inheritance: + + +.. autoclass:: tmdbapis.objs.pagination.RecommendedTVShows + :members: + :show-inheritance: + + +.. autoclass:: tmdbapis.objs.pagination.SearchCollections + :members: + :show-inheritance: + + +.. autoclass:: tmdbapis.objs.pagination.SearchCompanies + :members: + :show-inheritance: + + +.. autoclass:: tmdbapis.objs.pagination.SearchKeywords + :members: + :show-inheritance: + + +.. autoclass:: tmdbapis.objs.pagination.SearchMovies + :members: + :show-inheritance: + + +.. autoclass:: tmdbapis.objs.pagination.SearchMulti + :members: + :show-inheritance: + + +.. autoclass:: tmdbapis.objs.pagination.SearchPeople + :members: + :show-inheritance: + + +.. autoclass:: tmdbapis.objs.pagination.SearchTVShows + :members: + :show-inheritance: + + +.. autoclass:: tmdbapis.objs.pagination.SimilarMovies + :members: + :show-inheritance: + + +.. autoclass:: tmdbapis.objs.pagination.SimilarTVShows + :members: + :show-inheritance: + + +.. autoclass:: tmdbapis.objs.pagination.TaggedImages + :members: + :show-inheritance: + + +.. autoclass:: tmdbapis.objs.pagination.TMDbList + :members: + :show-inheritance: + + +.. autoclass:: tmdbapis.objs.pagination.TopRatedMovies + :members: + :show-inheritance: + + +.. autoclass:: tmdbapis.objs.pagination.TopRatedTVShows + :members: + :show-inheritance: + + +.. autoclass:: tmdbapis.objs.pagination.Trending + :members: + :show-inheritance: + + +.. autoclass:: tmdbapis.objs.pagination.TVShowRecommendations + :members: + :show-inheritance: + + +.. autoclass:: tmdbapis.objs.pagination.TVShowsAiringToday + :members: + :show-inheritance: + + +.. autoclass:: tmdbapis.objs.pagination.TVShowsOnTheAir + :members: + :show-inheritance: + + +.. autoclass:: tmdbapis.objs.pagination.TVShowWatchlist + :members: + :show-inheritance: + + +.. autoclass:: tmdbapis.objs.pagination.UpcomingMovies + :members: + :show-inheritance: + diff --git a/docs/objs/reload.rst b/docs/objs/reload.rst new file mode 100644 index 0000000..d5b302c --- /dev/null +++ b/docs/objs/reload.rst @@ -0,0 +1,81 @@ +************** +Reload Objects +************** + + +.. autoclass:: tmdbapis.objs.reload.TMDbReload + :members: + :show-inheritance: + + +.. autoclass:: tmdbapis.objs.reload.Account + :members: + :show-inheritance: + + +.. autoclass:: tmdbapis.objs.reload.Collection + :members: + :show-inheritance: + + +.. autoclass:: tmdbapis.objs.reload.Company + :members: + :show-inheritance: + + +.. autoclass:: tmdbapis.objs.reload.Configuration + :members: + :show-inheritance: + + +.. autoclass:: tmdbapis.objs.reload.Credit + :members: + :show-inheritance: + + +.. autoclass:: tmdbapis.objs.reload.Episode + :members: + :show-inheritance: + :inherited-members: + + +.. autoclass:: tmdbapis.objs.reload.EpisodeGroup + :members: + :show-inheritance: + + +.. autoclass:: tmdbapis.objs.reload.Keyword + :members: + :show-inheritance: + + +.. autoclass:: tmdbapis.objs.reload.Movie + :members: + :show-inheritance: + :inherited-members: + + +.. autoclass:: tmdbapis.objs.reload.Network + :members: + :show-inheritance: + + +.. autoclass:: tmdbapis.objs.reload.Person + :members: + :show-inheritance: + + +.. autoclass:: tmdbapis.objs.reload.Review + :members: + :show-inheritance: + + +.. autoclass:: tmdbapis.objs.reload.Season + :members: + :show-inheritance: + + +.. autoclass:: tmdbapis.objs.reload.TVShow + :members: + :show-inheritance: + :inherited-members: diff --git a/docs/objs/simple.rst b/docs/objs/simple.rst new file mode 100644 index 0000000..da432c7 --- /dev/null +++ b/docs/objs/simple.rst @@ -0,0 +1,96 @@ +************** +Simple Objects +************** + +.. autoclass:: tmdbapis.objs.base.TMDbObj + :members: + + +.. autoclass:: tmdbapis.objs.simple.AlternativeName + :members: + :show-inheritance: + + +.. autoclass:: tmdbapis.objs.simple.AlternativeTitle + :members: + :show-inheritance: + + +.. autoclass:: tmdbapis.objs.simple.Certification + :members: + :show-inheritance: + + +.. autoclass:: tmdbapis.objs.simple.Country + :members: + :show-inheritance: + + +.. autoclass:: tmdbapis.objs.simple.CountryCertifications + :members: + :show-inheritance: + + +.. autoclass:: tmdbapis.objs.simple.CountryWatchProviders + :members: + :show-inheritance: + + +.. autoclass:: tmdbapis.objs.simple.Department + :members: + :show-inheritance: + + +.. autoclass:: tmdbapis.objs.simple.FindResults + :members: + :show-inheritance: + + +.. autoclass:: tmdbapis.objs.simple.Genre + :members: + :show-inheritance: + + +.. autoclass:: tmdbapis.objs.simple.Group + :members: + :show-inheritance: + + +.. autoclass:: tmdbapis.objs.simple.Language + :members: + :show-inheritance: + + +.. autoclass:: tmdbapis.objs.simple.ReleaseDate + :members: + :show-inheritance: + + +.. autoclass:: tmdbapis.objs.simple.Timezones + :members: + :show-inheritance: + + +.. autoclass:: tmdbapis.objs.simple.Trailer + :members: + :show-inheritance: + + +.. autoclass:: tmdbapis.objs.simple.Translation + :members: + :show-inheritance: + + +.. autoclass:: tmdbapis.objs.simple.User + :members: + :show-inheritance: + + +.. autoclass:: tmdbapis.objs.simple.Video + :members: + :show-inheritance: + + +.. autoclass:: tmdbapis.objs.simple.WatchProvider + :members: + :show-inheritance: diff --git a/docs/rawapi.rst b/docs/rawapi.rst new file mode 100644 index 0000000..adb10ca --- /dev/null +++ b/docs/rawapi.rst @@ -0,0 +1,14 @@ + +Raw API 3 +========================================================== + +.. automodule:: tmdbapis.api3 + :members: + :show-inheritance: + +Raw API 4 +========================================================== + +.. automodule:: tmdbapis.api4 + :members: + :show-inheritance: \ No newline at end of file diff --git a/docs/toc.rst b/docs/toc.rst new file mode 100644 index 0000000..2e1eba1 --- /dev/null +++ b/docs/toc.rst @@ -0,0 +1,24 @@ + +.. toctree:: + :caption: Overview + :titlesonly: + + intro + +.. toctree:: + :caption: Modules + :titlesonly: + + objapi + rawapi + exceptions + + +.. toctree:: + :caption: Objects + :titlesonly: + + objs/simple + objs/reload + objs/image + objs/pagination diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..f229360 --- /dev/null +++ b/requirements.txt @@ -0,0 +1 @@ +requests diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..1412805 --- /dev/null +++ b/setup.py @@ -0,0 +1,49 @@ +import os, tmdbapis + +from setuptools import setup, find_packages + +with open("README.rst", "r") as f: + long_descr = f.read() + +__version__ = None +if os.path.exists("VERSION"): + with open("VERSION") as handle: + for line in handle.readlines(): + line = line.strip() + if len(line) > 0: + __version__ = line + break + +setup( + name=tmdbapis.__package_name__, + version=__version__, + description=tmdbapis.__description__, + long_description=long_descr, + url=tmdbapis.__url__, + author=tmdbapis.__author__, + author_email=tmdbapis.__email__, + license=tmdbapis.__license__, + packages=find_packages(), + python_requires=">=3.6", + keywords=["tmdbapis", "tmdbapi", "tmdb", "wrapper", "api"], + install_requires=[ + "requests" + ], + project_urls={ + "Documentation": "https://tmdbapis.readthedocs.io/en/latest/", + "Funding": "https://github.com/sponsors/meisnate12", + "Source": "https://github.com/meisnate12/TMDbAPIs", + "Issues": "https://github.com/meisnate12/TMDbAPIs/issues", + }, + classifiers=[ + "Development Status :: 5 - Production/Stable", + "Intended Audience :: Developers", + "Topic :: Software Development :: Libraries", + "Programming Language :: Python", + "Programming Language :: Python :: 3.6", + "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + ] +) + diff --git a/tests/test_api.py b/tests/test_api.py new file mode 100644 index 0000000..afe5fd0 --- /dev/null +++ b/tests/test_api.py @@ -0,0 +1,161 @@ +import os, unittest + +from tmdbapis import NotFound, Movie, Person, TVShow +from tmdbapis.tmdb import TMDbAPIs + +import logging +logger = logging.getLogger() +logger.setLevel(logging.DEBUG) +logger.addHandler(logging.StreamHandler()) + +class APITests(unittest.TestCase): + @classmethod + def setUpClass(cls): + session_id = os.environ["TMDB_SESSION"] + apikey = os.environ["TMDB_APIKEY"] + access = os.environ["TMDB_V4"] + cls.api = TMDbAPIs(apikey, v4_access_token=access, session_id=session_id) + + def test_ab_variables(self): + self.assertEqual(self.api.account_id, 8568268) + self.assertEqual(self.api.v4_account_id, "5d339fb42f8d097bccd118c7") + + def test_account(self): + account = self.api.account() + self.assertEqual(account.id, 8568268) + self.assertEqual(account.username, "meisnate12") + self.assertGreater(len(account.created_lists(v3=True).results), 0) + self.assertGreater(len(account.created_lists().results), 0) + self.assertGreater(len(account.favorite_movies(v3=True).results), 0) + self.assertGreater(len(account.favorite_movies().results), 0) + self.assertGreater(len(account.favorite_tv_shows(v3=True).results), 0) + self.assertGreater(len(account.favorite_tv_shows().results), 0) + self.assertGreater(len(account.rated_movies(v3=True).results), 0) + self.assertGreater(len(account.rated_movies().results), 0) + self.assertGreater(len(account.rated_tv_shows(v3=True).results), 0) + self.assertGreater(len(account.rated_tv_shows().results), 0) + self.assertGreater(len(account.rated_episodes().results), 0) + self.assertGreater(len(account.movie_watchlist(v3=True).results), 0) + self.assertGreater(len(account.movie_watchlist().results), 0) + self.assertGreater(len(account.tv_show_watchlist(v3=True).results), 0) + self.assertGreater(len(account.tv_show_watchlist().results), 0) + self.assertGreater(len(account.movie_recommendations().results), 0) + self.assertGreater(len(account.tv_show_recommendations().results), 0) + + def test_certifications(self): + movie = self.api.movie_certifications() + tv = self.api.tv_certifications() + self.assertIn("US", movie) + self.assertIn("US", tv) + + def test_changes(self): + self.assertGreater(len(self.api.movie_change_list()), 0) + self.assertGreater(len(self.api.tv_change_list()), 0) + self.assertGreater(len(self.api.person_change_list()), 0) + + def test_collections(self): + collection = self.api.collection(10) + self.assertEqual(collection.name, "Star Wars Collection") + self.assertGreater(len(collection.movies), 0) + + def test_companies(self): + company = self.api.company(1) + self.assertEqual(company.name, "Lucasfilm") + self.assertGreater(len(company.movies), 0) + + def test_credit(self): + credit = self.api.credit("52fe420dc3a36847f8000441") + self.assertEqual(credit.name, "Mark Hamill") + self.assertEqual(credit.person_id, 2) + self.assertEqual(credit.character, "Luke Skywalker") + + def test_discover(self): + movies = self.api.discover_movies(with_companies=1) + self.assertGreater(len(movies), 0) + tv_shows = self.api.discover_tv_shows(with_companies=1) + self.assertGreater(len(tv_shows), 0) + + def test_find(self): + results = self.api.find_by_id(imdb_id="tt0076759") + self.assertGreater(len(results.movie_results), 0) + + def test_genres(self): + self.assertGreater(len(self.api.movie_genres(reload=True)), 0) + self.assertGreater(len(self.api.tv_genres(reload=True)), 0) + + def test_keyword(self): + keyword = self.api.keyword(4270) + self.assertEqual(keyword.name, "galaxy") + + def test_list(self): + test_list = self.api.create_list("PMM Test", "en", load=True) + test_list.add_items((524434, "movie")) + test_list.reload() + self.assertGreater(len(test_list), 0) + self.assertTrue(test_list.has_item(524434, "movie")) + test_list.remove_items((524434, "movie")) + self.assertFalse(test_list.has_item(524434, "movie")) + test_list.add_items((524434, "movie")) + test_list.clear() + self.assertFalse(test_list.has_item(524434, "movie")) + test_list.reload() + self.assertEqual(len(test_list), 0) + test_list.delete() + with self.assertRaises(NotFound): + test_list.reload() + + def test_movie(self): + movie = self.api.movie(11) + self.assertEqual(movie.title, "Star Wars") + self.assertIsInstance(self.api.latest_movie(), Movie) + self.assertGreater(len(self.api.now_playing_movies()), 0) + self.assertGreater(len(self.api.popular_movies()), 0) + self.assertGreater(len(self.api.top_rated_movies()), 0) + self.assertGreater(len(self.api.upcoming_movies()), 0) + + + def test_network(self): + network = self.api.network(1) + self.assertEqual(network.name, "Fuji TV") + + def test_trending(self): + self.assertGreater(len(self.api.trending("movie", "day")), 0) + self.assertGreater(len(self.api.trending("tv", "day")), 0) + self.assertGreater(len(self.api.trending("movie", "week")), 0) + self.assertGreater(len(self.api.trending("tv", "week")), 0) + + def test_person(self): + person = self.api.person(1) + self.assertEqual(person.name, "George Lucas") + self.assertIsInstance(self.api.latest_person(), Person) + self.assertGreater(len(self.api.popular_people()), 0) + + def test_review(self): + review = self.api.review("58a231c5925141179e000674") + self.assertEqual(review.author, "Cat Ellington") + + def test_tv_show(self): + show = self.api.tv_show(4194) + self.assertEqual(show.name, "Star Wars: The Clone Wars") + self.assertIsInstance(self.api.latest_tv(), TVShow) + self.assertGreater(len(self.api.tv_airing_today()), 0) + self.assertGreater(len(self.api.tv_on_the_air()), 0) + self.assertGreater(len(self.api.popular_tv()), 0) + self.assertGreater(len(self.api.top_rated_tv()), 0) + + def test_tv_season(self): + season = self.api.tv_season(4194, 1) + self.assertEqual(season.name, "Season 1") + + def test_tv_episode(self): + episode = self.api.tv_episode(4194, 1, 1) + self.assertEqual(episode.name, "Ambush") + + def test_episode_group(self): + episode_group = self.api.episode_group("5b11ba820e0a265847002c6e") + self.assertEqual(episode_group.name, "Chronological Order") + + def test_providers(self): + self.assertGreater(len(self.api.provider_regions()), 0) + self.assertGreater(len(self.api.movie_providers()), 0) + self.assertGreater(len(self.api.tv_providers()), 0) \ No newline at end of file diff --git a/tmdbapis/__init__.py b/tmdbapis/__init__.py new file mode 100644 index 0000000..04f334e --- /dev/null +++ b/tmdbapis/__init__.py @@ -0,0 +1,71 @@ +import pkg_resources + +from tmdbapis.tmdb import TMDbAPIs +from tmdbapis.objs.simple import AlternativeName, AlternativeTitle, Certification, Country, CountryCertifications, \ + CountryWatchProviders, Department, FindResults, Genre, Group, Language, ReleaseDate, Timezones, Trailer, \ + Translation, User, Video, WatchProvider +from tmdbapis.objs.image import TMDbImage, Backdrop, Logo, Poster, Profile, Still, Tagged +from tmdbapis.objs.reload import TMDbReload, Account, Collection, Company, Configuration, Credit, Episode, \ + EpisodeGroup, Keyword, Movie, Network, Person, Review, Season, TVShow +from tmdbapis.exceptions import TMDbException, Invalid, NotFound, Unauthorized, Authentication + +try: + __version__ = pkg_resources.get_distribution("tmdbapis").version +except pkg_resources.DistributionNotFound: + __version__ = "" +__author__ = "Nathan Taggart" +__credits__ = "meisnate12" +__package_name__ = "tmdbapis" +__project_name__ = "TMDbAPIs" +__description__ = "A lightweight Python library for The V3 and V4 TMDb APIs." +__url__ = "https://github.com/meisnate12/TMDbAPIs" +__email__ = "meisnate12@gmail.com" +__license__ = "MIT License" +__all__ = [ + "TMDbAPIs", + "AlternativeName", + "AlternativeTitle", + "Certification", + "Country", + "CountryCertifications", + "CountryWatchProviders", + "Department", + "FindResults", + "Genre", + "Group", + "Language", + "ReleaseDate", + "Timezones", + "Trailer", + "Translation", + "User", + "Video", + "WatchProvider", + "TMDbImage", + "Backdrop", + "Logo", + "Poster", + "Profile", + "Still", + "Tagged", + "TMDbReload", + "Account", + "Collection", + "Company", + "Configuration", + "Credit", + "Episode", + "EpisodeGroup", + "Keyword", + "Movie", + "Network", + "Person", + "Review", + "Season", + "TVShow", + "TMDbException", + "Invalid", + "NotFound", + "Unauthorized", + "Authentication" +] diff --git a/tmdbapis/api3.py b/tmdbapis/api3.py new file mode 100644 index 0000000..9b482a6 --- /dev/null +++ b/tmdbapis/api3.py @@ -0,0 +1,2884 @@ +import logging +from json.decoder import JSONDecodeError +from typing import Dict, Optional, Union + +from requests import Session, Response +from requests.exceptions import RequestException + +from tmdbapis.api4 import API4 +from tmdbapis.exceptions import TMDbException, NotFound, Unauthorized, WritePermission, PrivateResource, Authentication + +logger = logging.getLogger(__name__) + +base_url = "https://api.themoviedb.org/3" + +class API3: + """ Raw V3 API Class containing all `TMDb API3 calls `__. + + Parameters: + apikey (str): TMDb V3 API Key. + session_id (Optional[str]): TMDb V3 Session ID. + api4 (Optional[API4]): :class:`tmdbapis.api4.API4` object. + session (Optional[Session]): :class:`requests.Session` object. + validate (bool): Validate the TMDb V3 API Key on creation. + + Attributes: + apikey (str): TMDb V3 API Key. + session_id (str): TMDb V3 Session ID. + account_id (str): TMDb V3 Account ID. + response (Response): TMDb V3 most recent full :class:`requests.Response` object. + """ + def __init__(self, apikey: str, session_id: Optional[str] = None, api4: Optional[API4] = None, + session: Optional[Session] = None, validate: bool = True): + self.apikey = apikey + self._api4 = api4 + self._session_id = session_id + self._account_id = None + self._session = Session() if session is None else session + self.response = None + if validate: + self.configuration_get_api_configuration() + + def _get(self, path, **kwargs): + """ process get request. """ + return self._request("get", path, **kwargs) + + def _delete(self, path, json=None, **kwargs): + """ process delete request. """ + return self._request("delete", path, json=json, **kwargs) + + def _post(self, path, json=None, **kwargs): + """ process post request. """ + return self._request("post", path, json=json, **kwargs) + + def _put(self, path, json=None, **kwargs): + """ process put request. """ + return self._request("put", path, json=json, **kwargs) + + def _request(self, request_type, path, json=None, **kwargs): + """ process request. """ + url_params = {"api_key": f"{self.apikey}"} + for key, value in kwargs.items(): + if value is not None: + url_params[key] = value + request_url = f"{base_url}{path}" + logger.debug(f"Request URL: {request_url}") + if json is not None: + logger.debug(f"Request JSON {json}") + try: + if request_type == "delete": + self.response = self._session.delete(request_url, json=json, params=url_params) + elif request_type == "post": + self.response = self._session.post(request_url, json=json, params=url_params) + elif request_type == "put": + self.response = self._session.put(request_url, json=json, params=url_params) + else: + self.response = self._session.get(request_url, params=url_params) + response_json = self.response.json() + except (RequestException, JSONDecodeError): + raise TMDbException(f"Failed to Connect to {base_url}") + logger.debug(f"Response ({self.response.status_code} [{self.response.reason}]) {response_json}") + if self.response.status_code == 401: + if "status_code" in response_json: + if response_json["status_code"] == 36: + raise WritePermission("Requires V4 Authentication, use tmdbapis.v4_authenticate(), then approve the returned URL, and finally run tmdbapis.v4_approved()") + elif response_json["status_code"] == 39: + raise PrivateResource(response_json["status_message"]) + elif response_json["status_code"] == 7: + raise Unauthorized(response_json["status_message"]) + else: + raise TMDbException(f"({response_json['status_code']}) {response_json['status_message']}") + else: + raise TMDbException(f"({self.response.status_code} [{self.response.reason}]) {response_json}") + elif self.response.status_code == 404: + raise NotFound(f"({self.response.status_code} [{self.response.reason}]) Requested Item Not Found") + elif self.response.status_code >= 400: + raise TMDbException(f"({self.response.status_code} [{self.response.reason}]) {response_json}") + elif "errors" in response_json: + raise TMDbException(response_json["errors"]) + elif "success" in response_json and response_json["success"] is False: + raise TMDbException(response_json["status_message"]) + return response_json + + @property + def session_id(self): + if not self._session_id: + if self._api4 and self._api4.has_write_token: + self.authentication_create_session_from_v4(self._api4.access_token) + if not self._session_id: + raise Authentication("Requires Authentication, use tmdbapis.authenticate(username, password)") + return self._session_id + + @property + def account_id(self): + if not self._account_id: + self._account_id = self.account_get_details(self.session_id)["id"] + return self._account_id + + def account_get_details( + self, + session_id: Optional[str] = None, + append_to_response: Optional[str] = None + ) -> Dict: + """ `Account Get Details `__ + + Get your account details. + + Supports ``append_to_response``. Read more about this `here `__. + + Parameters: + session_id (Optional[str]): Session ID. + append_to_response (Optional[str]): Append other requests to the response. + """ + return self._get( + "/account", + session_id=session_id if session_id else self.session_id, + append_to_response=append_to_response + ) + + def account_get_created_lists( + self, + session_id: Optional[str] = None, + account_id: Optional[str] = None, + language: Optional[str] = None, + page: Optional[int] = None + ) -> Dict: + """ `Account Get Created Lists `__ + + Get all of the lists created by an account. Will include private lists if you are the owner. + + Parameters: + session_id (Optional[str]): Session ID. + account_id (Optional[str]): Account ID. + language (Optional[str]): ISO-639-1 or ISO-3166-1 value to display translated data for the fields that support it. + page (Optional[int]): Specify which page to query. + """ + return self._get( + f"/account/{account_id if account_id else self.account_id}/lists", + session_id=session_id if session_id else self.session_id, + language=language, + page=page + ) + + def account_get_favorite_movies( + self, + session_id: Optional[str] = None, + account_id: Optional[str] = None, + language: Optional[str] = None, + sort_by: Optional[str] = None, + page: Optional[str] = None + ) -> Dict: + """ `Account Get Favorite Movies `__ + + Get the list of your favorite movies. + + Parameters: + session_id (Optional[str]): Session ID. + account_id (Optional[str]): Account ID. + language (Optional[str]): ISO-639-1 or ISO-3166-1 value to display translated data for the fields that support it. + sort_by (Optional[str]): Sort the results. Allowed Values: ``created_at.asc`` and ``created_at.desc`` + page (Optional[int]): Specify which page to query. + """ + return self._get( + f"/account/{account_id if account_id else self.account_id}/favorite/movies", + session_id=session_id if session_id else self.session_id, + language=language, + sort_by=sort_by, + page=page + ) + + def account_get_favorite_tv_shows( + self, + session_id: Optional[str] = None, + account_id: Optional[str] = None, + language: Optional[str] = None, + sort_by: Optional[str] = None, + page: Optional[str] = None + ) -> Dict: + """ `Account Get Favorite TV Shows `__ + + Get the list of your favorite TV shows. + + Parameters: + session_id (Optional[str]): Session ID. + account_id (Optional[str]): Account ID. + language (Optional[str]): ISO-639-1 or ISO-3166-1 value to display translated data for the fields that support it. + sort_by (Optional[str]): Sort the results. Allowed Values: ``created_at.asc`` and ``created_at.desc`` + page (Optional[int]): Specify which page to query. + """ + return self._get( + f"/account/{account_id if account_id else self.account_id}/favorite/tv", + session_id=session_id if session_id else self.session_id, + language=language, + sort_by=sort_by, + page=page + ) + + def account_mark_as_favorite( + self, media_type: str, media_id: int, favorite: bool, + session_id: Optional[str] = None, + account_id: Optional[str] = None + ) -> Dict: + """ `Account Mark as Favorite `__ + + This method allows you to mark a movie or TV show as a favorite item. + + Parameters: + session_id (Optional[str]): Session ID. + account_id (Optional[str]): Account ID. + media_type (str): Media Type. Allowed Values: ``movie`` and ``tv`` + media_id (int): Media ID. + favorite (bool): Mark or Remove as favorite. + """ + return self._post( + f"/account/{account_id if account_id else self.account_id}/favorite", + json={ + "media_type": media_type, + "media_id": media_id, + "favorite": favorite + }, + session_id=session_id if session_id else self.session_id + ) + + def account_get_rated_movies( + self, + session_id: Optional[str] = None, + account_id: Optional[str] = None, + language: Optional[str] = None, + sort_by: Optional[str] = None, + page: Optional[str] = None + ) -> Dict: + """ `Account Get Rated Movies `__ + + Get a list of all the movies you have rated. + + Parameters: + session_id (Optional[str]): Session ID. + account_id (Optional[str]): Account ID. + language (Optional[str]): ISO-639-1 or ISO-3166-1 value to display translated data for the fields that support it. + sort_by (Optional[str]): Sort the results. Allowed Values: ``created_at.asc`` and ``created_at.desc`` + page (Optional[int]): Specify which page to query. + """ + return self._get( + f"/account/{account_id if account_id else self.account_id}/rated/movies", + session_id=session_id if session_id else self.session_id, + language=language, + sort_by=sort_by, + page=page + ) + + def account_get_rated_tv_shows( + self, + session_id: Optional[str] = None, + account_id: Optional[str] = None, + language: Optional[str] = None, + sort_by: Optional[str] = None, + page: Optional[str] = None + ) -> Dict: + """ `Account Get Rated TV Shows `__ + + Get a list of all the TV shows you have rated. + + Parameters: + session_id (Optional[str]): Session ID. + account_id (Optional[str]): Account ID. + language (Optional[str]): ISO-639-1 or ISO-3166-1 value to display translated data for the fields that support it. + sort_by (Optional[str]): Sort the results. Allowed Values: ``created_at.asc`` and ``created_at.desc`` + page (Optional[int]): Specify which page to query. + """ + return self._get( + f"/account/{account_id if account_id else self.account_id}/rated/tv", + session_id=session_id if session_id else self.session_id, + language=language, + sort_by=sort_by, + page=page + ) + + def account_get_rated_tv_episodes( + self, + session_id: Optional[str] = None, + account_id: Optional[str] = None, + language: Optional[str] = None, + sort_by: Optional[str] = None, + page: Optional[str] = None + ) -> Dict: + """ `Account Get Rated TV Episodes `__ + + Get a list of all the TV episodes you have rated. + + Parameters: + session_id (Optional[str]): Session ID. + account_id (Optional[str]): Account ID. + language (Optional[str]): ISO-639-1 or ISO-3166-1 value to display translated data for the fields that support it. + sort_by (Optional[str]): Sort the results. Allowed Values: ``created_at.asc`` and ``created_at.desc`` + page (Optional[int]): Specify which page to query. + """ + return self._get( + f"/account/{account_id if account_id else self.account_id}/rated/tv/episodes", + session_id=session_id if session_id else self.session_id, + language=language, + sort_by=sort_by, + page=page + ) + + def account_get_movie_watchlist( + self, + session_id: Optional[str] = None, + account_id: Optional[str] = None, + language: Optional[str] = None, + sort_by: Optional[str] = None, + page: Optional[str] = None + ) -> Dict: + """ `Account Get Movie Watchlist `__ + + Get a list of all the movies you have added to your watchlist. + + Parameters: + session_id (Optional[str]): Session ID. + account_id (Optional[str]): Account ID. + language (Optional[str]): ISO-639-1 or ISO-3166-1 value to display translated data for the fields that support it. + sort_by (Optional[str]): Sort the results. Allowed Values: ``created_at.asc`` and ``created_at.desc`` + page (Optional[int]): Specify which page to query. + """ + return self._get( + f"/account/{account_id if account_id else self.account_id}/watchlist/movies", + session_id=session_id if session_id else self.session_id, + language=language, + sort_by=sort_by, + page=page + ) + + def account_get_tv_show_watchlist( + self, + session_id: Optional[str] = None, + account_id: Optional[str] = None, + language: Optional[str] = None, + sort_by: Optional[str] = None, + page: Optional[str] = None + ) -> Dict: + """ `Account Get TV Show Watchlist `__ + + Get a list of all the TV shows you have added to your watchlist. + + Parameters: + session_id (Optional[str]): Session ID. + account_id (Optional[str]): Account ID. + language (Optional[str]): ISO-639-1 or ISO-3166-1 value to display translated data for the fields that support it. + sort_by (Optional[str]): Sort the results. Allowed Values: ``created_at.asc`` and ``created_at.desc`` + page (Optional[int]): Specify which page to query. + """ + return self._get( + f"/account/{account_id if account_id else self.account_id}/watchlist/tv", + session_id=session_id if session_id else self.session_id, + language=language, + sort_by=sort_by, + page=page + ) + + def account_add_to_watchlist( + self, media_type: str, media_id: int, watchlist: bool, + session_id: Optional[str] = None, + account_id: Optional[str] = None + ) -> Dict: + """ `Account Add to Watchlist `__ + + Add a movie or TV show to your watchlist. + + Parameters: + session_id (Optional[str]): Session ID. + account_id (Optional[str]): Account ID. + media_type (str): Media Type. Allowed Values: ``movie`` and ``tv`` + media_id (int): Media ID. + watchlist (bool): Add or remove from your Watchlist. + """ + return self._post( + f"/account/{account_id if account_id else self.account_id}/watchlist", + json={ + "media_type": media_type, + "media_id": media_id, + "watchlist": watchlist + }, + session_id=session_id if session_id else self.session_id + ) + + def authentication_create_guest_session(self) -> Dict: + """ `Authentication Create Guest Session `__ + + This method will let you create a new guest session. Guest sessions are a type of session that will let a + user rate movies and TV shows but not require them to have a TMDB user account. More information about user + authentication can be found + `here `__. + + Please note, you should only generate a single guest session per user (or device) as you will be able to + attach the ratings to a TMDB user account in the future. There is also IP limits in place so you should + always make sure it's the end user doing the guest session actions. + + If a guest session is not used for the first time within 24 hours, it will be automatically deleted. + """ + return self._get("/authentication/guest_session/new") + + def authentication_create_request_token(self) -> Dict: + """ `Authentication Create Request Token `__ + + Create a temporary request token that can be used to validate a TMDB user login. More details about how this + works can be found + `here `__. + """ + return self._get("/authentication/token/new") + + def authentication_create_session(self, request_token: str) -> Dict: + """ `Authentication Create Session `__ + + You can use this method to create a fully valid session ID once a user has validated the request token. More + information about how this works can be found + `here `__. + + Parameters: + request_token (str): Request Token. + """ + response = self._post("/authentication/session/new", json={"request_token": request_token}) + self._session_id = response["session_id"] + return response + + def authentication_create_session_with_login(self, username, password, request_token) -> Dict: + """ `Authentication Create Session With Login `__ + + This method allows an application to validate a request token by entering a username and password. + + Not all applications have access to a web view so this can be used as a substitute. + + Please note, the preferred method of validating a request token is to have a user authenticate the request + via the TMDB website. You can read about that method + `here `__. + + Parameters: + username (str): TMDb Username. + password (str): TMDb Password. + request_token (str): Request Token. + """ + return self._post( + "/authentication/token/validate_with_login", + json={ + "username": username, + "password": password, + "request_token": request_token + } + ) + + def authentication_create_session_from_v4(self, access_token: str) -> Dict: + """ `Authentication Create Session (from v4 access token) `__ + + Use this method to create a v3 session ID if you already have a valid v4 access token. The v4 token needs to + be authenticated by the user. Your standard "read token" will not validate to create a session ID. + + Parameters: + access_token (str): V4 Approved Access Token. + """ + response = self._post("/authentication/session/convert/4", json={"access_token": access_token}) + self._session_id = response["session_id"] + return response + + def authentication_delete_session(self, session_id: Optional[str] = None) -> Dict: + """ `Authentication Delete Session `__ + + If you would like to delete (or "logout") from a session, call this method with a valid session ID. + + Parameters: + session_id (Optional[str]): Session ID. + """ + return self._delete("/authentication/session", json={"session_id": session_id if session_id else self.session_id}) + + def certifications_get_movie_certifications(self) -> Dict: + """ `Certifications Get Movie Certifications `__ + + Get an up to date list of the officially supported movie certifications on TMDB. + """ + return self._get("/certification/movie/list") + + def certifications_get_tv_certifications(self) -> Dict: + """ `Certifications Get TV Certifications `__ + + Get an up to date list of the officially supported TV show certifications on TMDB. + """ + return self._get("/certification/tv/list") + + def changes_get_movie_change_list(self, start_date: Optional[str] = None, end_date: Optional[str] = None) -> Dict: + """ `Changes Get Movie Change List `__ + + Get a list of all of the movie ids that have been changed in the past 24 hours. + + You can query it for up to 14 days worth of changed IDs at a time with the ``start_date`` and ``end_date`` + query parameters. + + Parameters: + start_date (Optional[str]): Filter the results with a start date. Format: YYYY-MM-DD + end_date (Optional[str]): Filter the results with an end date. Format: YYYY-MM-DD + """ + return self._get("/movie/changes", start_date=start_date, end_date=end_date) + + def changes_get_tv_change_list(self, start_date: Optional[str] = None, end_date: Optional[str] = None) -> Dict: + """ `Changes Get TV Change List `__ + + Get a list of all of the TV show ids that have been changed in the past 24 hours. + + You can query it for up to 14 days worth of changed IDs at a time with the ``start_date`` and ``end_date`` + query parameters. + + Parameters: + start_date (Optional[str]): Filter the results with a start date. Format: YYYY-MM-DD + end_date (Optional[str]): Filter the results with an end date. Format: YYYY-MM-DD + """ + return self._get("/tv/changes", start_date=start_date, end_date=end_date) + + def changes_get_person_change_list(self, start_date: Optional[str] = None, end_date: Optional[str] = None) -> Dict: + """ `Changes Get Person Change List `__ + + Get a list of all of the person ids that have been changed in the past 24 hours. + + You can query it for up to 14 days worth of changed IDs at a time with the ``start_date`` and ``end_date`` + query parameters. + + Parameters: + start_date (Optional[str]): Filter the results with a start date. Format: YYYY-MM-DD + end_date (Optional[str]): Filter the results with an end date. Format: YYYY-MM-DD + """ + return self._get("/person/changes", start_date=start_date, end_date=end_date) + + def collections_get_details( + self, collection_id: int, + language: Optional[str] = None, + append_to_response: Optional[str] = None, + include_image_language: Optional[str] = None + ) -> Dict: + """ `Collections Get Details `__ + + Get collection details by id. + + Supports ``append_to_response``. Read more about this `here `__. + + Parameters: + collection_id (int): Collection ID. + language (Optional[str]): ISO-639-1 or ISO-3166-1 value to display translated data for the fields that support it. + append_to_response (Optional[str]): Comma-separated list of sub requests within the same namespace. + include_image_language (Optional[str]): Comma-separated list of ISO-639-1 values or null to query for additional image languages. (Used with ``images`` append_to_response) + """ + return self._get( + f"/collection/{collection_id}", + language=language, + append_to_response=append_to_response, + include_image_language=include_image_language + ) + + def collections_get_images( + self, collection_id: int, + language: Optional[str] = None, + include_image_language: Optional[str] = None + ) -> Dict: + """ `Collections Get Images `__ + + Get the images for a collection by id. + + Parameters: + collection_id (int): Collection ID. + language (Optional[str]): ISO-639-1 or ISO-3166-1 value to display translated data for the fields that support it. + include_image_language (Optional[str]): Comma-separated list of ISO-639-1 values or null to query for additional image languages. + """ + return self._get( + f"/collection/{collection_id}/images", + language=language, + include_image_language=include_image_language + ) + + def collections_get_translations( + self, collection_id: int, + language: Optional[str] = None + ) -> Dict: + """ `Collections Get Translations `__ + + Get the list translations for a collection by id. + + Parameters: + collection_id (int): Collection ID. + language (Optional[str]): ISO-639-1 or ISO-3166-1 value to display translated data for the fields that support it. + """ + return self._get(f"/collection/{collection_id}/translations", language=language) + + def companies_get_details( + self, company_id: int, + language: Optional[str] = None, + append_to_response: Optional[str] = None, + page: Optional[int] = None + ) -> Dict: + """ `Companies Get Details `__ + + Get a companies details by id. + + Supports ``append_to_response``. Read more about this `here `__. + + Parameters: + company_id (int): Company ID. + language (Optional[str]): ISO-639-1 or ISO-3166-1 value to display translated data for the fields that support it. + append_to_response (Optional[str]): Comma-separated list of sub requests within the same namespace. + page (Optional[int]): Specify which page to query. (Used with ``movies`` append_to_response) + """ + return self._get( + f"/company/{company_id}", + language=language, + append_to_response=append_to_response, + page=page + ) + + def companies_get_alternative_names(self, company_id: int) -> Dict: + """ `Companies Get Alternative Names `__ + + Get the alternative names of a company. + + Parameters: + company_id (int): Company ID. + """ + return self._get(f"/company/{company_id}/alternative_names") + + def companies_get_images(self, company_id: int) -> Dict: + """ `Companies Get Images `__ + + Get a companies logos by id. + + There are two image formats that are supported for companies, PNG's and SVG's. You can see which type the + original file is by looking at the ``file_type`` field. We prefer SVG's as they are resolution independent and + as such, the width and height are only there to reflect the original asset that was uploaded. An SVG can be + scaled properly beyond those dimensions if you call them as a PNG. + + For more information about how SVG's and PNG's can be used, take a read through + `this document `__. + + Parameters: + company_id (int): Company ID. + """ + return self._get(f"/company/{company_id}/images") + + def companies_get_movies( + self, company_id: int, + language: Optional[str] = None, + page: Optional[int] = None + ) -> Dict: + """ Companies Get Movies + + Get a companies movies by id. + + Parameters: + company_id (int): Company ID. + language (Optional[str]): ISO-639-1 or ISO-3166-1 value to display translated data for the fields that support it. + page (Optional[int]): Specify which page to query. + """ + return self._get(f"/company/{company_id}/movies", language=language, page=page) + + def configuration_get_api_configuration(self, append_to_response: Optional[str] = None) -> Dict: + """ `Configuration Get API3 Configuration `__ + + Get the system wide configuration information. Some elements of the API3 require some knowledge of this + configuration data. The purpose of this is to try and keep the actual API3 responses as light as possible. + It is recommended you cache this data within your application and check for updates every few days. + + This method currently holds the data relevant to building image URLs as well as the change key map. + + To build an image URL, you will need 3 pieces of data. The ``base_url``, ``size`` and ``file_path``. Simply + combine them all and you will have a fully qualified URL. Here’s an example URL: + + .. code-block:: + + https://image.tmdb.org/t/p/w500/8uO0gUM8aNqYLs1OsTBQiXu0fEv.jpg + + The configuration method also contains the list of change keys which can be useful if you are building an + app that consumes data from the change feed. + + Supports ``append_to_response``. Read more about this `here `__. + + Parameters: + append_to_response (Optional[str]): Comma-separated list of sub requests within the same namespace. + """ + return self._get("/configuration", append_to_response=append_to_response) + + def configuration_get_countries(self) -> Dict: + """ `Configuration Get Countries `__ + + Get the list of countries (ISO 3166-1 tags) used throughout TMDB. + """ + return self._get("/configuration/countries") + + def configuration_get_jobs(self) -> Dict: + """ `Configuration Get Jobs `__ + + Get a list of the jobs and departments we use on TMDB. + """ + return self._get("/configuration/jobs") + + def configuration_get_languages(self) -> Dict: + """ `Configuration Get Languages `__ + + Get the list of languages (ISO 639-1 tags) used throughout TMDB. + """ + return self._get("/configuration/languages") + + def configuration_get_primary_translations(self) -> Dict: + """ `Configuration Get Primary Translations `__ + + Get a list of the officially supported translations on TMDB. + + While it's technically possible to add a translation in any one of the + `languages `__ we have added to TMDB + (we don't restrict content), the ones listed in this method are the ones we also support for localizing + the website with which means they are what we refer to as the "primary" translations. + + These are all specified as `IETF tags `__ to identify the + languages we use on TMDB. There is one exception which is image languages. They are currently only + designated by a ISO-639-1 tag. This is a planned upgrade for the future. + + We're always open to adding more if you think one should be added. You can ask about getting a new primary + translation added by posting on + `the forums `__. + + One more thing to mention, these are the translations that map to our website translation project. You can + view and contribute to that project `here `__ + """ + return self._get("/configuration/primary_translations") + + def configuration_get_timezones(self) -> Dict: + """ `Configuration Get Timezones `__ + + Get the list of timezones used throughout TMDB. + """ + return self._get("/configuration/timezones") + + def credits_get_details(self, credit_id: str) -> Dict: + """ `Credits Get Details `__ + + Get a movie or TV credit details by id. + + Parameters: + credit_id (int): Credit ID. + """ + return self._get(f"/credit/{credit_id}") + + def discover_movie_discover(self, **kwargs) -> Dict: + """ `Discover Movie Discover `__ + + Discover movies by different types of data like average rating, number of votes, genres and certifications. + You can get a valid list of certifications from the + `certifications list `__ + method. + + Discover also supports a nice list of sort options. See below for all of the available options. + + Please note, when using ``certification`` \\ ``certification.lte`` you must also specify + ``certification_country``. These two parameters work together in order to filter the results. You can only + filter results with the countries we have added to our + `certifications list `__. + + If you specify the ``region`` parameter, the regional release date will be used instead of the primary + release date. The date returned will be the first date based on your query (ie. if a ``with_release_type`` + is specified). It's important to note the order of the release types that are used. Specifying "2|3" would + return the limited theatrical release date as opposed to "3|2" which would return the theatrical date. + + Also note that a number of filters support being comma (``,``) or pipe (``|``) separated. Comma's are + treated like an ``AND`` and query while pipe's are an ``OR``. + + Some examples of what can be done with discover can be found + `here `__. + + ``.`` cannot be included directly in the function parameters so the parameters must be provided as a + kwargs dictionary. + + Parameters: + language (Optional[str]): ISO-639-1 or ISO-3166-1 value to display translated data for the fields that support it. + region (Optional[str]): ISO-3166-1 code to filter release dates. Must be uppercase. + sort_by (Optional[str]): Allowed Values: ``popularity.asc``, ``popularity.desc``, ``release_date.asc``, ``release_date.desc``, ``revenue.asc``, ``revenue.desc``, ``primary_release_date.asc``, ``primary_release_date.desc``, ``original_title.asc``, ``original_title.desc``, ``vote_average.asc``, ``vote_average.desc``, ``vote_count.asc``, ``vote_count.desc`` + certification_country (Optional[str]): Used in conjunction with the ``certification`` filter, use this to specify a country with a valid certification. + certification (Optional[str]): Filter results with a valid certification from the ``certification_country`` field. + certification.lte (Optional[str]): Filter and only include movies that have a certification that is less than or equal to the specified value. + certification.gte (Optional[str]): Filter and only include movies that have a certification that is greater than or equal to the specified value. + include_adult (Optional[bool]): A filter and include or exclude adult movies. + include_video (Optional[bool]): A filter to include or exclude videos. + page (Optional[int]): Specify the page of results to query. + primary_release_year (Optional[int): A filter to limit the results to a specific primary release year. + primary_release_date.gte (Optional[str]): Filter and only include movies that have a primary release date that is greater or equal to the specified value. Format: YYYY-MM-DD + primary_release_date.lte (Optional[str]): Filter and only include movies that have a primary release date that is less than or equal to the specified value. Format: YYYY-MM-DD + release_date.gte (Optional[str]): Filter and only include movies that have a release date (looking at all release dates) that is greater or equal to the specified value. Format: YYYY-MM-DD + release_date.lte (Optional[str]): Filter and only include movies that have a release date (looking at all release dates) that is less than or equal to the specified value. Format: YYYY-MM-DD + with_release_type (Optional[int]): Specify a comma (AND) or pipe (OR) separated value to filter release types by. These release types map to the same values found on the movie release date method. + year (Optional[int]): A filter to limit the results to a specific year (looking at all release dates). + vote_count.gte (Optional[int]): Filter and only include movies that have a vote count that is greater or equal to the specified value. + vote_count.lte (Optional[int]): Filter and only include movies that have a vote count that is less than or equal to the specified value. + vote_average.gte (Optional[float]): Filter and only include movies that have a rating that is greater or equal to the specified value. + vote_average.lte (Optional[float]): Filter and only include movies that have a rating that is less than or equal to the specified value. + with_cast (Optional[str]): A comma separated list of person ID's. Only include movies that have one of the ID's added as an actor. + with_crew (Optional[str]): A comma separated list of person ID's. Only include movies that have one of the ID's added as a crew member. + with_people (Optional[str]): A comma separated list of person ID's. Only include movies that have one of the ID's added as a either a actor or a crew member. + with_companies (Optional[str]): A comma separated list of production company ID's. Only include movies that have one of the ID's added as a production company. + with_genres (Optional[str]): Comma separated value of genre ids that you want to include in the results. + without_genres (Optional[str]): Comma separated value of genre ids that you want to exclude from the results. + with_keywords (Optional[str]): A comma separated list of keyword ID's. Only includes movies that have one of the ID's added as a keyword. + without_keywords (Optional[str]): Exclude items with certain keywords. You can comma and pipe separate these values to create an 'AND' or 'OR' logic. + with_runtime.gte (Optional[int]): Filter and only include movies that have a runtime that is greater or equal to a value. + with_runtime.lte (Optional[int]): Filter and only include movies that have a runtime that is less than or equal to a value. + with_original_language (Optional[str]): Specify an ISO 639-1 string to filter results by their original language value. + with_watch_providers (Optional[str]): A comma or pipe separated list of watch provider ID's. Combine this filter with ``watch_region`` in order to filter your results by a specific watch provider in a specific region. + watch_region (Optional[str]): An ISO 3166-1 code. Combine this filter with ``with_watch_providers`` in order to filter your results by a specific watch provider in a specific region. + with_watch_monetization_types (Optional[str]): In combination with ``watch_region``, you can filter by monetization type. Allowed Values: ``flatrate``, ``free``, ``ads``, ``rent``, ``buy`` + """ + return self._get("/discover/movie", **kwargs) + + def discover_tv_discover(self, **kwargs) -> Dict: + """ `Discover TV Discover `__ + + Discover TV shows by different types of data like average rating, number of votes, genres, the network they + aired on and air dates. + + Discover also supports a nice list of sort options. See below for all of the available options. + + Also note that a number of filters support being comma (``,``) or pipe (``|``) separated. Comma's are + treated like an ``AND`` and query while pipe's are an ``OR``. + + Some examples of what can be done with discover can be found + `here `__. + + ``.`` cannot be included directly in the function parameters so the parameters must be provided as a + kwargs dictionary. + + Parameters: + language (Optional[str]): ISO-639-1 or ISO-3166-1 value to display translated data for the fields that support it. + sort_by (Optional[str]): Allowed Values: ``vote_average.desc``, ``vote_average.asc``, ``first_air_date.desc``, ``first_air_date.asc``, ``popularity.desc``, ``popularity.asc`` + air_date.gte (Optional[str]): Filter and only include TV shows that have a air date (by looking at all episodes) that is greater or equal to the specified value. Format: YYYY-MM-DD + air_date.lte (Optional[str]): Filter and only include TV shows that have a air date (by looking at all episodes) that is less than or equal to the specified value. Format: YYYY-MM-DD + first_air_date.gte (Optional[str]): Filter and only include TV shows that have a original air date that is greater or equal to the specified value. Can be used in conjunction with the ``include_null_first_air_dates`` filter if you want to include items with no air date. Format: YYYY-MM-DD + first_air_date.lte (Optional[str]): Filter and only include TV shows that have a original air date that is less than or equal to the specified value. Can be used in conjunction with the ``include_null_first_air_dates`` filter if you want to include items with no air date. Format: YYYY-MM-DD + first_air_date_year (Optional[int]): Filter and only include TV shows that have a original air date year that equal to the specified value. Can be used in conjunction with the ``include_null_first_air_dates`` filter if you want to include items with no air date. + page (Optional[int]): Specify the page of results to query. + timezone (Optional[str]): Used in conjunction with the ``air_date.gte``/``air_date.lte`` filter to calculate the proper UTC offset. + vote_average.gte (Optional[float]): Filter and only include TV shows that have a rating that is greater or equal to the specified value. + vote_average.lte (Optional[float]): Filter and only include TV shows that have a rating that is less than or equal to the specified value. + vote_count.gte (Optional[int]): Filter and only include TV shows that have a vote count that is greater or equal to the specified value. + vote_count.lte (Optional[int]): Filter and only include TV shows that have a vote count that is less than or equal to the specified value. + with_genres (Optional[str]): Comma separated value of genre ids that you want to include in the results. + with_networks (Optional[str]): Comma separated value of network ids that you want to include in the results. + without_genres (Optional[str]): Comma separated value of genre ids that you want to exclude from the results. + with_runtime.gte (Optional[int]): Filter and only include TV shows with an episode runtime that is greater than or equal to a value. + with_runtime.lte (Optional[int]): Filter and only include TV shows with an episode runtime that is less than or equal to a value. + include_null_first_air_dates (Optional[bool]): Use this filter to include TV shows that don't have an air date while using any of the ``first_air_date`` filters. + with_original_language (Optional[str]): Specify an ISO 639-1 string to filter results by their original language value. + without_keywords (Optional[str]): Exclude items with certain keywords. You can comma and pipe separate these values to create an 'AND' or 'OR' logic. + screened_theatrically (Optional[bool]): Filter results to include items that have been screened theatrically. + with_companies (Optional[str]): A comma separated list of production company ID's. Only include movies that have one of the ID's added as a production company. + with_keywords (Optional[str]): A comma separated list of keyword ID's. Only includes TV shows that have one of the ID's added as a keyword. + with_watch_providers (Optional[str]): A comma or pipe separated list of watch provider ID's. Combine this filter with ``watch_region`` in order to filter your results by a specific watch provider in a specific region. + watch_region (Optional[str]): An ISO 3166-1 code. Combine this filter with ``with_watch_providers`` in order to filter your results by a specific watch provider in a specific region. + with_watch_monetization_types (Optional[str]): In combination with ``watch_region``, you can filter by monetization type. Allowed Values: ``flatrate``, ``free``, ``ads``, ``rent``, ``buy`` + """ + return self._get("/discover/tv", **kwargs) + + def find_find_by_id( + self, + external_id: Union[int, str], + external_source: str, + language: Optional[str] = None + ) -> Dict: + """ `FindResults FindResults by ID `__ + + The find method makes it easy to search for objects in our database by an external id. + + This method will search all objects (movies, TV shows and people) and return the results in a single response. + + The supported external sources for each object are as follows. + + .. list-table:: Media Databases + :header-rows: 1 + + * - + - Movies + - TV Shows + - TV Seasons + - TV Episodes + - People + * - IMDb ID + - ✓ + - ✓ + - ✗ + - ✓ + - ✓ + * - TVDB ID + - ✗ + - ✓ + - ✓ + - ✓ + - ✗ + * - Freebase MID* + - ✗ + - ✓ + - ✓ + - ✓ + - ✓ + * - Freebase ID* + - ✗ + - ✓ + - ✓ + - ✓ + - ✓ + * - TVRage ID* + - ✗ + - ✓ + - ✓ + - ✓ + - ✓ + + .. list-table:: Social IDs + :header-rows: 1 + + * - + - Movies + - TV Shows + - TV Seasons + - TV Episodes + - People + * - Facebook + - ✓ + - ✓ + - ✗ + - ✗ + - ✓ + * - Instagram + - ✓ + - ✓ + - ✗ + - ✗ + - ✓ + * - Twitter + - ✓ + - ✓ + - ✗ + - ✗ + - ✓ + + \\* Defunct or no longer available as a service. + + Parameters: + external_id (Union[int, str]): External ID. + external_source (str): External Source. Allowed Values: ``imdb_id``, ``freebase_mid``, ``freebase_id``, ``tvdb_id``, ``tvrage_id``, ``facebook_id``, ``twitter_id``, ``instagram_id`` + language (Optional[str]): ISO-639-1 or ISO-3166-1 value to display translated data for the fields that support it. + """ + return self._get( + f"/find/{external_id}", + external_source=external_source, + language=language + ) + + def genres_get_movie_list(self, language: Optional[str] = None) -> Dict: + """ `Genres Get Movie List `__ + + Get the list of official genres for movies. + + Parameters: + language (Optional[str]): ISO-639-1 or ISO-3166-1 value to display translated data for the fields that support it. + """ + return self._get("/genre/movie/list", language=language) + + def genres_get_tv_list(self, language: Optional[str] = None) -> Dict: + """ `Genres Get TV List `__ + + Get the list of official genres for TV shows. + + Parameters: + language (Optional[str]): ISO-639-1 or ISO-3166-1 value to display translated data for the fields that support it. + """ + return self._get("/genre/tv/list", language=language) + + def guest_sessions_get_rated_movies( + self, guest_session_id: str, + language: Optional[str] = None, + sort_by: Optional[str] = None, + page: Optional[str] = None + ) -> Dict: + """ `Guest Sessions Get Rated Movies `__ + + Get the rated movies for a guest session. + + Parameters: + guest_session_id (str): Guest Session ID. + language (Optional[str]): ISO-639-1 or ISO-3166-1 value to display translated data for the fields that support it. + sort_by (Optional[str]): Sort the results. Allowed Values: ``created_at.asc`` and ``created_at.desc`` + page (Optional[int]): Specify which page to query. + """ + return self._get( + f"/guest_session/{guest_session_id}/rated/movies", + language=language, + sort_by=sort_by, + page=page + ) + + def guest_sessions_get_rated_tv_shows( + self, guest_session_id: str, + language: Optional[str] = None, + sort_by: Optional[str] = None, + page: Optional[str] = None + ) -> Dict: + """ `Guest Sessions Get Rated TV Shows `__ + + Get the rated TV shows for a guest session. + + Parameters: + guest_session_id (str): Guest Session ID. + language (Optional[str]): ISO-639-1 or ISO-3166-1 value to display translated data for the fields that support it. + sort_by (Optional[str]): Sort the results. Allowed Values: ``created_at.asc`` and ``created_at.desc`` + page (Optional[int]): Specify which page to query. + """ + return self._get( + f"/guest_session/{guest_session_id}/rated/tv", + language=language, + sort_by=sort_by, + page=page + ) + + def guest_sessions_get_rated_tv_episodes( + self, guest_session_id: str, + language: Optional[str] = None, + sort_by: Optional[str] = None, + page: Optional[str] = None + ) -> Dict: + """ `Guest Sessions Get Rated TV Episodes `__ + + Get the rated TV episodes for a guest session. + + Parameters: + guest_session_id (str): Guest Session ID. + language (Optional[str]): ISO-639-1 or ISO-3166-1 value to display translated data for the fields that support it. + sort_by (Optional[str]): Sort the results. Allowed Values: ``created_at.asc`` and ``created_at.desc`` + page (Optional[int]): Specify which page to query. + """ + return self._get( + f"/guest_session/{guest_session_id}/rated/tv/episodes", + language=language, + sort_by=sort_by, + page=page + ) + + def keywords_get_details( + self, keyword_id: int, + language: Optional[str] = None, + append_to_response: Optional[str] = None, + include_adult: Optional[bool] = None + ) -> Dict: + """ `Keywords Get Details `__ + + Get keyword details by id. + + Supports ``append_to_response``. Read more about this `here `__. + + Parameters: + keyword_id (int): Keyword ID. + language (Optional[str]): ISO-639-1 or ISO-3166-1 value to display translated data for the fields that support it. + append_to_response (Optional[str]): Comma-separated list of sub requests within the same namespace. + include_adult (Optional[bool]): Choose whether to include adult (pornography) content in the results. (Used with ``movies`` append_to_response) + """ + return self._get( + f"/keyword/{keyword_id}", + append_to_response=append_to_response, + language=language, + include_adult=include_adult + ) + + def keywords_get_movies( + self, keyword_id: int, + language: Optional[str] = None, + include_adult: Optional[bool] = None + ) -> Dict: + """ `Keywords Get Movies `__ + + Get the movies that belong to a keyword. + + Parameters: + keyword_id (int): Keyword ID. + language (Optional[str]): ISO-639-1 or ISO-3166-1 value to display translated data for the fields that support it. + include_adult (Optional[bool]): Choose whether to include adult (pornography) content in the results. + """ + return self._get(f"/keyword/{keyword_id}/movies", language=language, include_adult=include_adult) + + def lists_get_details(self, list_id: int, language: Optional[str] = None) -> Dict: + """ `Lists Get Details `__ + + Get the details of a list. + + Parameters: + list_id (int): List ID. + language (Optional[str]): ISO-639-1 or ISO-3166-1 value to display translated data for the fields that support it. + """ + return self._get(f"/list/{list_id}", language=language) + + def lists_check_item_status(self, list_id: int, movie_id: int) -> Dict: + """ `Lists Check Item Status `__ + + You can use this method to check if a movie has already been added to the list. + + Parameters: + list_id (int): List ID. + movie_id (int): Movie ID to check list for. + """ + return self._get(f"/list/{list_id}/item_status", movie_id=movie_id) + + def lists_create_list( + self, + session_id: Optional[str] = None, + name: Optional[str] = "", + description: Optional[str] = "", + language: Optional[str] = "" + ) -> Dict: + """ `Lists Create List `__ + + Create a list. + + Parameters: + session_id (Optional[str]): Session ID. + name (Optional[str]): List Name. + description (Optional[str]): List Description. + language (Optional[str]): Set the ISO-639-1 variant for your list. + """ + return self._post( + "/list", + json={ + "name": name, + "description": description, + "language": language + }, + session_id=session_id if session_id else self.session_id + ) + + def lists_add_movie( + self, list_id: int, movie_id: int, + session_id: Optional[str] = None + ) -> Dict: + """ `Lists Add Movie `__ + + Add a movie to a list. + + Parameters: + list_id (int): List ID. + movie_id (int): Movie ID to check list for. + session_id (Optional[str]): Session ID. + """ + return self._post( + f"/list/{list_id}/add_item", + json={"media_id": movie_id}, + session_id=session_id if session_id else self.session_id + ) + + def lists_remove_movie(self, list_id: int, movie_id: int, session_id: Optional[str] = None) -> Dict: + """ `Lists Remove Movie `__ + + Remove a movie from a list. + + Parameters: + list_id (int): List ID. + movie_id (int): Movie ID to check list for. + session_id (Optional[str]): Session ID. + """ + return self._post( + f"/list/{list_id}/remove_item", + json={"media_id": movie_id}, + session_id=session_id if session_id else self.session_id + ) + + def lists_clear_list(self, list_id: int, confirm: bool, session_id: Optional[str] = None) -> Dict: + """ `Lists Clear List `__ + + Clear all of the items from a list. + + Parameters: + list_id (int): List ID. + confirm (bool): Confirm you want to clear the list. + session_id (Optional[str]): Session ID. + """ + return self._post( + f"/list/{list_id}/clear", + session_id=session_id if session_id else self.session_id, + confirm=confirm + ) + + def lists_delete_list(self, list_id: int, session_id: Optional[str] = None) -> Dict: + """ `Lists Delete List `__ + + Delete a list. + + Parameters: + list_id (int): List ID. + session_id (Optional[str]): Session ID. + """ + return self._delete(f"/list/{list_id}", session_id=session_id if session_id else self.session_id) + + def movies_get_details( + self, movie_id: int, + language: Optional[str] = None, + append_to_response: Optional[str] = None, + session_id: Optional[str] = None, + guest_session_id: Optional[str] = None, + country: Optional[str] = None, + start_date: Optional[str] = None, + end_date: Optional[str] = None, + include_image_language: Optional[str] = None, + include_video_language: Optional[str] = None, + page: Optional[int] = None + ) -> Dict: + """ `Movies Get Details `__ + + Get the primary information about a movie. + + Supports ``append_to_response``. Read more about this `here `__. + + Parameters: + movie_id (int): Movie ID. + language (Optional[str]): ISO-639-1 or ISO-3166-1 value to display translated data for the fields that support it. + append_to_response (Optional[str]): Comma-separated list of sub requests within the same namespace. + session_id (Optional[str]): Session ID. (Used with ``account_states`` append_to_response) + guest_session_id (Optional[str]): Guest Session ID. (Used with ``account_states`` append_to_response) + country (Optional[str]): ISO-3166-1 value to display alternative titles. (Used with ``alternative_titles`` append_to_response) + start_date (Optional[str]): Filter the results with a start date. Format: YYYY-MM-DD (Used with ``changes`` append_to_response) + end_date (Optional[str]): Filter the results with an end date. Format: YYYY-MM-DD (Used with ``changes`` append_to_response) + include_image_language (Optional[str]): Comma-separated list of ISO-639-1 values or null to query for additional image languages. (Used with ``images`` append_to_response) + include_video_language (Optional[str]): Comma-separated list of ISO-639-1 values or null to query for additional video languages. (Used with ``videos`` append_to_response) + page (Optional[int]): Specify which page to query. (Used with ``lists`` append_to_response) + """ + return self._get( + f"/movie/{movie_id}", + language=language, + append_to_response=append_to_response, + session_id=session_id if session_id else self._session_id, + guest_session_id=guest_session_id, + country=country, + start_date=start_date, + end_date=end_date, + include_image_language=include_image_language, + include_video_language=include_video_language, + page=page + ) + + def movies_get_account_states( + self, movie_id: int, + session_id: Optional[str] = None, + guest_session_id: Optional[str] = None + ) -> Dict: + """ `Movies Get Account States `__ + + Grab the following account states for a session: + + * Movie rating + * If it belongs to your watchlist + * If it belongs to your favourite list + + Parameters: + movie_id (int): Movie ID. + session_id (Optional[str]): Session ID. + guest_session_id (Optional[str]): Guest Session ID. + """ + return self._get( + f"/movie/{movie_id}/account_states", + session_id=session_id if session_id else self.session_id, + guest_session_id=guest_session_id + ) + + def movies_get_alternative_titles(self, movie_id: int, country: Optional[str] = None) -> Dict: + """ `Movies Get Alternative Titles `__ + + Get all of the alternative titles for a movie. + + Parameters: + movie_id (int): Movie ID. + country (Optional[str]): Session ID. + """ + return self._get(f"/movie/{movie_id}/alternative_titles", country=country) + + def movies_get_changes( + self, movie_id: int, + start_date: Optional[str] = None, + end_date: Optional[str] = None, + page: Optional[int] = None + ) -> Dict: + """ `Movies Get Changes `__ + + Get the changes for a movie. By default only the last 24 hours are returned. + + You can query up to 14 days in a single query by using the ``start_date`` and ``end_date`` query parameters. + + Parameters: + movie_id (int): Movie ID. + start_date (Optional[str]): Filter the results with a start date. Format: YYYY-MM-DD + end_date (Optional[str]): Filter the results with an end date. Format: YYYY-MM-DD + page (Optional[int]): Specify which page to query. + """ + return self._get(f"/movie/{movie_id}/changes", start_date=start_date, end_date=end_date, page=page) + + def movies_get_credits(self, movie_id: int, language: Optional[str] = None) -> Dict: + """ `Movies Get Credits `__ + + Get the cast and crew for a movie. + + Parameters: + movie_id (int): Movie ID. + language (Optional[str]): ISO-639-1 or ISO-3166-1 value to display translated data for the fields that support it. + """ + return self._get(f"/movie/{movie_id}/credits", language=language) + + def movies_get_external_ids(self, movie_id: int) -> Dict: + """ `Movies Get External IDs `__ + + Get the external ids for a movie. We currently support the following external sources. + + .. list-table:: + :header-rows: 1 + + * - Media Databases + - Social IDs + * - IMDb ID + - Facebook + * - + - Instagram + * - + - Twitter + + Parameters: + movie_id (int): Movie ID. + """ + return self._get(f"/movie/{movie_id}/external_ids") + + def movies_get_images( + self, movie_id: int, + language: Optional[str] = None, + include_image_language: Optional[str] = None + ) -> Dict: + """ `Movies Get Images `__ + + Get the images that belong to a movie. + + Querying images with a ``language`` parameter will filter the results. If you want to include a fallback + language (especially useful for backdrops) you can use the ``include_image_language`` parameter. This should be + a comma separated value like so: ``include_image_language=en,null``. + + Parameters: + movie_id (int): Movie ID. + language (Optional[str]): ISO-639-1 or ISO-3166-1 value to display translated data for the fields that support it. + include_image_language (Optional[str]): Comma-separated list of ISO-639-1 values or null to query for additional image languages. + """ + return self._get( + f"/movie/{movie_id}/images", + language=language, + include_image_language=include_image_language + ) + + def movies_get_keywords(self, movie_id: int) -> Dict: + """ `Movies Get Keywords `__ + + Get the keywords that have been added to a movie. + + Parameters: + movie_id (int): Movie ID. + """ + return self._get(f"/movie/{movie_id}/keywords") + + def movies_get_lists(self, movie_id: int, language: Optional[str] = None, page: Optional[int] = None) -> Dict: + """ `Movies Get Lists `__ + + Get a list of lists that this movie belongs to. + + Parameters: + movie_id (int): Movie ID. + language (Optional[str]): ISO-639-1 or ISO-3166-1 value to display translated data for the fields that support it. + page (Optional[int]): Specify which page to query. + """ + return self._get(f"/movie/{movie_id}/lists", language=language, page=page) + + def movies_get_recommendations(self, movie_id: int, language: Optional[str] = None, page: Optional[int] = None) -> Dict: + """ `Movies Get Recommendations `__ + + Get a list of recommended movies for a movie. + + Parameters: + movie_id (int): Movie ID. + language (Optional[str]): ISO-639-1 or ISO-3166-1 value to display translated data for the fields that support it. + page (Optional[int]): Specify which page to query. + """ + return self._get(f"/movie/{movie_id}/recommendations", language=language, page=page) + + def movies_get_release_dates(self, movie_id: int) -> Dict: + """ `Movies Get Release Dates `__ + + Get the release date along with the certification for a movie. + + Release dates support different types: + + 1. Premiere + 2. Theatrical (limited) + 3. Theatrical + 4. Digital + 5. Physical + 6. TV + + Parameters: + movie_id (int): Movie ID. + """ + return self._get(f"/movie/{movie_id}/release_dates") + + def movies_get_reviews(self, movie_id: int, language: Optional[str] = None, page: Optional[int] = None) -> Dict: + """ `Movies Get Reviews `__ + + Get the user reviews for a movie. + + Parameters: + movie_id (int): Movie ID. + language (Optional[str]): ISO-639-1 or ISO-3166-1 value to display translated data for the fields that support it. + page (Optional[int]): Specify which page to query. + """ + return self._get(f"/movie/{movie_id}/reviews", language=language, page=page) + + def movies_get_similar_movies(self, movie_id: int, language: Optional[str] = None, page: Optional[int] = None) -> Dict: + """ `Movies Get Similar Movies `__ + + Get a list of similar movies. This is not the same as the "Recommendation" system you see on the website. + + These items are assembled by looking at keywords and genres. + + Parameters: + movie_id (int): Movie ID. + language (Optional[str]): ISO-639-1 or ISO-3166-1 value to display translated data for the fields that support it. + page (Optional[int]): Specify which page to query. + """ + return self._get(f"/movie/{movie_id}/similar", language=language, page=page) + + def movies_get_translations(self, movie_id: int) -> Dict: + """ `Movies Get Translations `__ + + Get a list of translations that have been created for a movie. + + Parameters: + movie_id (int): Movie ID. + """ + return self._get(f"/movie/{movie_id}/translations") + + def movies_get_videos( + self, movie_id: int, + language: Optional[str] = None, + include_video_language: Optional[str] = None + ) -> Dict: + """ `Movies Get Videos `__ + + Get the videos that have been added to a movie. + + Querying videos with a ``language`` parameter will filter the results. If you want to include a fallback + language you can use the ``include_video_language`` parameter. This should be a comma separated value like so: + ``include_video_language=en,null``. + + Parameters: + movie_id (int): Movie ID. + language (Optional[str]): ISO-639-1 or ISO-3166-1 value to display translated data for the fields that support it. + include_video_language (Optional[str]): Comma-separated list of ISO-639-1 values or null to query for additional video languages. + """ + return self._get( + f"/movie/{movie_id}/videos", + language=language, + include_video_language=include_video_language + ) + + def movies_get_watch_providers(self, movie_id: int) -> Dict: + """ `Movies Get Watch Providers `__ + + Powered by our partnership with JustWatch, you can query this method to get a list of the availabilities per country by provider. + + This is not going to return full deep links, but rather, it's just enough information to display what's available where. + + You can link to the provided TMDB URL to help support TMDB and provide the actual deep links to the content. + + Please note: In order to use this data you must attribute the source of the data as JustWatch. If we find any usage not complying with these terms we will revoke access to the API. + + Parameters: + movie_id (int): Movie ID. + """ + return self._get(f"/movie/{movie_id}/watch/providers") + + def movies_rate_movie( + self, movie_id: int, value: float, + session_id: Optional[str] = None, + guest_session_id: Optional[str] = None + ) -> Dict: + """ `Movies Rate Movie `__ + + Rate a movie. + + Parameters: + movie_id (int): Movie ID. + value (float): Rating for the Movie. + session_id (Optional[str]): Session ID. + guest_session_id (Optional[str]): Guest Session ID. + """ + return self._post( + f"/movie/{movie_id}/rating", + json={"value": value}, + session_id=session_id if session_id else self.session_id, + guest_session_id=guest_session_id + ) + + def movies_delete_rating( + self, movie_id: int, + session_id: Optional[str] = None, + guest_session_id: Optional[str] = None + ) -> Dict: + """ `Movies Delete Rating `__ + + Remove your rating for a movie. + + Parameters: + movie_id (int): Movie ID. + session_id (Optional[str]): Session ID. + guest_session_id (Optional[str]): Guest Session ID. + """ + return self._delete( + f"/movie/{movie_id}/rating", + session_id=session_id if session_id else self.session_id, + guest_session_id=guest_session_id + ) + + def movies_get_latest(self, language: Optional[str] = None) -> Dict: + """ `Movies Get Latest `__ + + Get the most newly created movie. This is a live response and will continuously change. + + Parameters: + language (Optional[str]): ISO-639-1 or ISO-3166-1 value to display translated data for the fields that support it. + """ + return self._get("/movie/latest", language=language) + + def movies_get_now_playing(self, language: Optional[str] = None, page: Optional[str] = None, region: Optional[str] = None) -> Dict: + """ `Movies Get Now Playing `__ + + Get a list of movies in theatres. This is a release type query that looks for all movies that have a release type of 2 or 3 within the specified date range. + + You can optionally specify a ``region`` parameter which will narrow the search to only look for theatrical release dates within the specified country. + + Parameters: + language (Optional[str]): ISO-639-1 or ISO-3166-1 value to display translated data for the fields that support it. + page (Optional[int]): Specify which page to query. + region (Optional[str]): ISO-3166-1 value to filter the search to only look for now playing dates within the specified country. + """ + return self._get("/movie/now_playing", language=language, page=page, region=region) + + def movies_get_popular(self, language: Optional[str] = None, page: Optional[str] = None, region: Optional[str] = None) -> Dict: + """ `Movies Get Popular `__ + + Get a list of the current popular movies on TMDB. This list updates daily. + + Parameters: + language (Optional[str]): ISO-639-1 or ISO-3166-1 value to display translated data for the fields that support it. + page (Optional[int]): Specify which page to query. + region (Optional[str]): ISO-3166-1 value to filter the search to only look for popular release dates within the specified country. + """ + return self._get("/movie/popular", language=language, page=page, region=region) + + def movies_get_top_rated(self, language: Optional[str] = None, page: Optional[str] = None, region: Optional[str] = None) -> Dict: + """ `Movies Get Top Rated `__ + + Get the top rated movies on TMDB. + + Parameters: + language (Optional[str]): ISO-639-1 or ISO-3166-1 value to display translated data for the fields that support it. + page (Optional[int]): Specify which page to query. + region (Optional[str]): ISO-3166-1 value to filter the search to only look for top rated release dates within the specified country. + """ + return self._get("/movie/top_rated", language=language, page=page, region=region) + + def movies_get_upcoming(self, language: Optional[str] = None, page: Optional[str] = None, region: Optional[str] = None) -> Dict: + """ `Movies Get Upcoming `__ + + Get a list of upcoming movies in theatres. This is a release type query that looks for all movies that have a release type of 2 or 3 within the specified date range. + + You can optionally specify a ``region`` parameter which will narrow the search to only look for theatrical release dates within the specified country. + + Parameters: + language (Optional[str]): ISO-639-1 or ISO-3166-1 value to display translated data for the fields that support it. + page (Optional[int]): Specify which page to query. + region (Optional[str]): ISO-3166-1 value to filter the search to only look for upcoming release dates within the specified country. + """ + return self._get("/movie/upcoming", language=language, page=page, region=region) + + def networks_get_details(self, network_id: int, append_to_response: Optional[str] = None) -> Dict: + """ `Networks Get Details `__ + + Get the details of a network. + + Supports ``append_to_response``. Read more about this `here `__. + + Parameters: + network_id (int): Network ID. + append_to_response (Optional[str]): Comma-separated list of sub requests within the same namespace. + """ + return self._get(f"/network/{network_id}", append_to_response=append_to_response) + + def networks_get_alternative_names(self, network_id: int) -> Dict: + """ `Networks Get Alternative Names `__ + + Get the alternative names of a network. + + Parameters: + network_id (int): Network ID. + """ + return self._get(f"/network/{network_id}/alternative_names") + + def networks_get_images(self, network_id: int) -> Dict: + """ `Networks Get Images `__ + + Get the TV network logos by id. + + There are two image formats that are supported for networks, PNG's and SVG's. You can see which type the + original file is by looking at the ``file_type`` field. We prefer SVG's as they are resolution independent and + as such, the width and height are only there to reflect the original asset that was uploaded. An SVG can be + scaled properly beyond those dimensions if you call them as a PNG. + + For more information about how SVG's and PNG's can be used, take a read through + `this document `__. + + Parameters: + network_id (int): Network ID. + """ + return self._get(f"/network/{network_id}/images") + + def trending_get_trending(self, media_type: str, time_window: str, page: Optional[str] = None) -> Dict: + """ `Trending Get Trending `__ + + Get the daily or weekly trending items. The daily trending list tracks items over the period of a day while + items have a 24 hour half life. The weekly list tracks items over a 7 day period, with a 7 day half life. + + .. list-table:: Valid Media Types + :header-rows: 1 + + * - Media Type + - Description + * - all + - Include all movies, TV shows and people in the results as a global trending list. + * - movie + - Show the trending movies in the results. + * - tv + - Show the trending TV shows in the results. + * - person + - Show the trending people in the results. + + .. list-table:: Valid Time Windows + :header-rows: 1 + + * - Time Window + - Description + * - day + - View the trending list for the day. + * - week + - View the trending list for the week. + + Parameters: + media_type (str): Trending media type. Allowed Values: ``all``, ``movie``, ``tv``, and ``person`` + time_window (str): Trending list time window. Allowed Values: ``day`` and ``week`` + page (Optional[int]): Specify which page to query. + """ + return self._get(f"/trending/{media_type}/{time_window}", page=page) + + def people_get_details( + self, + person_id: int, + language: Optional[str] = None, + append_to_response: Optional[str] = None, + start_date: Optional[str] = None, + end_date: Optional[str] = None, + page: Optional[int] = None + ) -> Dict: + """ `People Get Details `__ + + Get the primary person details by id. + + Supports ``append_to_response``. Read more about this `here `__. + + Parameters: + person_id (int): Person ID. + language (Optional[str]): ISO-639-1 or ISO-3166-1 value to display translated data for the fields that support it. + append_to_response (Optional[str]): Comma-separated list of sub requests within the same namespace. + start_date (Optional[str]): Filter the results with a start date. Format: YYYY-MM-DD (Used with ``changes`` append_to_response) + end_date (Optional[str]): Filter the results with an end date. Format: YYYY-MM-DD (Used with ``changes`` append_to_response) + page (Optional[int]): Specify which page to query. (Used with ``changes`` append_to_response) + """ + return self._get( + f"/person/{person_id}", + language=language, + append_to_response=append_to_response, + start_date=start_date, + end_date=end_date, + page=page + ) + + def people_get_changes( + self, + person_id: int, + start_date: Optional[str] = None, + end_date: Optional[str] = None, + page: Optional[int] = None + ) -> Dict: + """ `People Get Changes `__ + + Get the changes for a person. By default only the last 24 hours are returned. + + You can query up to 14 days in a single query by using the ``start_date`` and ``end_date`` query parameters. + + Parameters: + person_id (int): Person ID. + start_date (Optional[str]): Filter the results with a start date. Format: YYYY-MM-DD + end_date (Optional[str]): Filter the results with an end date. Format: YYYY-MM-DD + page (Optional[int]): Specify which page to query. + """ + return self._get(f"/person/{person_id}/changes", start_date=start_date, end_date=end_date, page=page) + + def people_get_movie_credits(self, person_id: int, language: Optional[str] = None) -> Dict: + """ `People Get Movie Credits `__ + + Get the movie credits for a person. + + Parameters: + person_id (int): Person ID. + language (Optional[str]): ISO-639-1 or ISO-3166-1 value to display translated data for the fields that support it. + """ + return self._get(f"/person/{person_id}/movie_credits", language=language) + + def people_get_tv_credits(self, person_id: int, language: Optional[str] = None) -> Dict: + """ `People Get TV Credits `__ + + Get the TV show credits for a person. + + Parameters: + person_id (int): Person ID. + language (Optional[str]): ISO-639-1 or ISO-3166-1 value to display translated data for the fields that support it. + """ + return self._get(f"/person/{person_id}/tv_credits", language=language) + + def people_get_combined_credits(self, person_id: int, language: Optional[str] = None) -> Dict: + """ `People Get Combined Credits `__ + + Get the movie and TV credits together in a single response. + + Parameters: + person_id (int): Person ID. + language (Optional[str]): ISO-639-1 or ISO-3166-1 value to display translated data for the fields that support it. + """ + return self._get(f"/person/{person_id}/combined_credits", language=language) + + def people_get_external_ids(self, person_id: int, language: Optional[str] = None) -> Dict: + """ `People Get External IDs `__ + + Get the external ids for a person. We currently support the following external sources. + + .. list-table:: + :header-rows: 1 + + * - External Sources + * - IMDb ID + * - Facebook + * - Freebase MID* + * - Freebase ID* + * - Instagram + * - TVRage ID* + * - Twitter + + \\* Defunct or no longer available as a service. + + Parameters: + person_id (int): Person ID. + language (Optional[str]): ISO-639-1 or ISO-3166-1 value to display translated data for the fields that support it. + """ + return self._get(f"/person/{person_id}/external_ids", language=language) + + def people_get_images(self, person_id: int) -> Dict: + """ `People Get Images `__ + + Get the images for a person. + + Parameters: + person_id (int): Person ID. + """ + return self._get(f"/person/{person_id}/images") + + def people_get_tagged_images(self, person_id: int, language: Optional[str] = None, page: Optional[int] = None) -> Dict: + """ `People Get Tagged Images `__ + + Get the images that this person has been tagged in. + + Parameters: + person_id (int): Person ID. + language (Optional[str]): ISO-639-1 or ISO-3166-1 value to display translated data for the fields that support it. + page (Optional[int]): Specify which page to query. + """ + return self._get(f"/person/{person_id}/tagged_images", language=language, page=page) + + def people_get_translations(self, person_id: int, language: Optional[str] = None) -> Dict: + """ `People Get Movie Credits `__ + + Get a list of translations that have been created for a person. + + Parameters: + person_id (int): Person ID. + language (Optional[str]): ISO-639-1 or ISO-3166-1 value to display translated data for the fields that support it. + """ + return self._get(f"/person/{person_id}/translations", language=language) + + def people_get_latest(self, language: Optional[str] = None) -> Dict: + """ `People Get Latest `__ + + Get the most newly created person. This is a live response and will continuously change. + + Parameters: + language (Optional[str]): ISO-639-1 or ISO-3166-1 value to display translated data for the fields that support it. + """ + return self._get(f"/person/latest", language=language) + + def people_get_popular(self, language: Optional[str] = None, page: Optional[str] = None) -> Dict: + """ `People Get Popular `__ + + Get the list of popular people on TMDB. This list updates daily. + + Parameters: + language (Optional[str]): ISO-639-1 or ISO-3166-1 value to display translated data for the fields that support it. + page (Optional[int]): Specify which page to query. + """ + return self._get(f"/person/popular", language=language, page=page) + + def reviews_get_details(self, review_id: str) -> Dict: + """ `Reviews Get Details `__ + + Retrieve the details of a movie or TV show review. + + Parameters: + review_id (str): Review ID. + """ + return self._get(f"/review/{review_id}") + + def search_search_companies(self, query: str, page: Optional[int] = None) -> Dict: + """ `Search Search Companies `__ + + Search for companies. + + Parameters: + query (str): Pass a text query to search. This value should be URI encoded. + page (Optional[int]): Specify which page to query. + """ + return self._get("/search/company", query=query, page=page) + + def search_search_collections(self, query: str, language: Optional[str] = None, page: Optional[int] = None) -> Dict: + """ `Search Search Collections `__ + + Search for collections. + + Parameters: + query (str): Pass a text query to search. This value should be URI encoded. + language (Optional[str]): ISO-639-1 or ISO-3166-1 value to display translated data for the fields that support it. + page (Optional[int]): Specify which page to query. + """ + return self._get("/search/collection", query=query, language=language, page=page) + + def search_search_keywords(self, query: str, page: Optional[int] = None) -> Dict: + """ `Search Search Keywords `__ + + Search for keywords. + + Parameters: + query (str): Pass a text query to search. This value should be URI encoded. + page (Optional[int]): Specify which page to query. + """ + return self._get("/search/keyword", query=query, page=page) + + def search_search_movies( + self, + query: str, + language: Optional[str] = None, + page: Optional[int] = None, + include_adult: Optional[bool] = None, + region: Optional[str] = None, + year: Optional[int] = None, + primary_release_year: Optional[int] = None + ) -> Dict: + """ `Search Search Movies `__ + + Search for movies. + + Parameters: + query (str): Pass a text query to search. This value should be URI encoded. + language (Optional[str]): ISO-639-1 or ISO-3166-1 value to display translated data for the fields that support it. + page (Optional[int]): Specify which page to query. + include_adult (Optional[bool]): Choose whether to include adult (pornography) content in the results. + region (Optional[str]): Specify a ISO 3166-1 code to filter release dates. Must be uppercase. + year (Optional[int]): Specify a year for the search. + primary_release_year (Optional[int]): Specify a primary release year for the search. + """ + return self._get( + "/search/movie", + query=query, + language=language, + page=page, + include_adult=include_adult, + region=region, + year=year, + primary_release_year=primary_release_year + ) + + def search_multi_search( + self, + query: str, + language: Optional[str] = None, + page: Optional[int] = None, + include_adult: Optional[bool] = None, + region: Optional[str] = None + ) -> Dict: + """ `Search Multi Search `__ + + Search multiple models in a single request. Multi search currently supports searching for movies, tv shows and people in a single request. + + Parameters: + query (str): Pass a text query to search. This value should be URI encoded. + language (Optional[str]): ISO-639-1 or ISO-3166-1 value to display translated data for the fields that support it. + page (Optional[int]): Specify which page to query. + include_adult (Optional[bool]): Choose whether to include adult (pornography) content in the results. + region (Optional[str]): Specify a ISO 3166-1 code to filter release dates. Must be uppercase. + """ + return self._get( + "/search/multi", + query=query, + language=language, + page=page, + include_adult=include_adult, + region=region + ) + + def search_search_people( + self, + query: str, + language: Optional[str] = None, + page: Optional[int] = None, + include_adult: Optional[bool] = None, + region: Optional[str] = None + ) -> Dict: + """ `Search Search People `__ + + Search for people. + + Parameters: + query (str): Pass a text query to search. This value should be URI encoded. + language (Optional[str]): ISO-639-1 or ISO-3166-1 value to display translated data for the fields that support it. + page (Optional[int]): Specify which page to query. + include_adult (Optional[bool]): Choose whether to include adult (pornography) content in the results. + region (Optional[str]): Specify a ISO 3166-1 code to filter release dates. Must be uppercase. + """ + return self._get( + "/search/person", + query=query, + language=language, + page=page, + include_adult=include_adult, + region=region + ) + + def search_search_tv_shows( + self, + query: str, + language: Optional[str] = None, + page: Optional[int] = None, + include_adult: Optional[bool] = None, + first_air_date_year: Optional[int] = None + ) -> Dict: + """ `Search Search TV Shows `__ + + Search for TV show. + + Parameters: + query (str): Pass a text query to search. This value should be URI encoded. + language (Optional[str]): ISO-639-1 or ISO-3166-1 value to display translated data for the fields that support it. + page (Optional[int]): Specify which page to query. + include_adult (Optional[bool]): Choose whether to include adult (pornography) content in the results. + first_air_date_year (Optional[int]): Specify a first air date year for the search. + """ + return self._get( + "/search/tv", + query=query, + language=language, + page=page, + include_adult=include_adult, + first_air_date_year=first_air_date_year + ) + + def tv_get_details( + self, tv_id: int, + language: Optional[str] = None, + append_to_response: Optional[str] = None, + session_id: Optional[str] = None, + guest_session_id: Optional[str] = None, + start_date: Optional[str] = None, + end_date: Optional[str] = None, + include_image_language: Optional[str] = None, + include_video_language: Optional[str] = None, + page: Optional[int] = None + ) -> Dict: + """ `TV Get Details `__ + + Get the primary TV show details by id. + + Supports ``append_to_response``. Read more about this `here `__. + + Parameters: + tv_id (int): TV show ID. + language (Optional[str]): ISO-639-1 or ISO-3166-1 value to display translated data for the fields that support it. + append_to_response (Optional[str]): Comma-separated list of sub requests within the same namespace. + session_id (Optional[str]): Session ID. (Used with ``account_states`` append_to_response) + guest_session_id (Optional[str]): Guest Session ID. (Used with ``account_states`` append_to_response) + start_date (Optional[str]): Filter the results with a start date. Format: YYYY-MM-DD (Used with ``changes`` append_to_response) + end_date (Optional[str]): Filter the results with an end date. Format: YYYY-MM-DD (Used with ``changes`` append_to_response) + include_image_language (Optional[str]): Comma-separated list of ISO-639-1 values or null to query for additional image languages. (Used with ``images`` append_to_response) + include_video_language (Optional[str]): Comma-separated list of ISO-639-1 values or null to query for additional video languages. (Used with ``videos`` append_to_response) + page (Optional[int]): Specify which page to query. (Used with ``lists`` append_to_response) + """ + return self._get( + f"/tv/{tv_id}", + language=language, + append_to_response=append_to_response, + session_id=session_id if session_id else self.session_id, + guest_session_id=guest_session_id, + start_date=start_date, + end_date=end_date, + include_image_language=include_image_language, + include_video_language=include_video_language, + page=page + ) + + def tv_get_account_states( + self, tv_id: int, + language: Optional[str] = None, + session_id: Optional[str] = None, + guest_session_id: Optional[str] = None + ) -> Dict: + """ `TV Get Account States `__ + + Grab the following account states for a session: + + * TV rating + * If it belongs to your watchlist + * If it belongs to your favourite list + + Parameters: + tv_id (int): TV ID. + language (Optional[str]): ISO-639-1 or ISO-3166-1 value to display translated data for the fields that support it. + session_id (Optional[str]): Session ID. + guest_session_id (Optional[str]): Guest Session ID. + """ + return self._get( + f"/tv/{tv_id}/account_states", + language=language, + session_id=session_id if session_id else self.session_id, + guest_session_id=guest_session_id + ) + + def tv_get_aggregate_credits(self, tv_id: int, language: Optional[str] = None) -> Dict: + """ `TV Get Aggregate Credits `__ + + Get the aggregate credits (cast and crew) that have been added to a TV show. + + This call differs from the main ``credits`` call in that it does not return the newest season but rather, is a view of all the entire cast & crew for all episodes belonging to a TV show. + + Parameters: + tv_id (int): TV ID. + language (Optional[str]): ISO-639-1 or ISO-3166-1 value to display translated data for the fields that support it. + """ + return self._get(f"/tv/{tv_id}/aggregate_credits", language=language) + + def tv_get_alternative_titles(self, tv_id: int) -> Dict: + """ `TV Get Alternative Titles `__ + + Returns all of the alternative titles for a TV show. + + Parameters: + tv_id (int): TV ID. + """ + return self._get(f"/tv/{tv_id}/alternative_titles") + + def tv_get_changes( + self, tv_id: int, + start_date: Optional[str] = None, + end_date: Optional[str] = None, + page: Optional[int] = None + ) -> Dict: + """ `TV Get Changes `__ + + Get the changes for a TV show. By default only the last 24 hours are returned. + + You can query up to 14 days in a single query by using the ``start_date`` and ``end_date`` query parameters. + + TV show changes are different than movie changes in that there are some edits on seasons and episodes that + will create a change entry at the show level. These can be found under the season and episode keys. These + keys will contain a ``series_id`` and ``episode_id``. You can use the :meth:`tv_seasons_get_changes` + and :meth:`tv_episodes_get_changes` methods to look these up individually. + + Parameters: + tv_id (int): TV ID. + start_date (Optional[str]): Filter the results with a start date. Format: YYYY-MM-DD + end_date (Optional[str]): Filter the results with an end date. Format: YYYY-MM-DD + page (Optional[int]): Specify which page to query. + """ + return self._get(f"/tv/{tv_id}/changes", start_date=start_date, end_date=end_date, page=page) + + def tv_get_content_ratings(self, tv_id: int, language: Optional[str] = None) -> Dict: + """ `TV Get Content Ratings `__ + + Get the list of content ratings (certifications) that have been added to a TV show. + + Parameters: + tv_id (int): TV ID. + language (Optional[str]): ISO-639-1 or ISO-3166-1 value to display translated data for the fields that support it. + """ + return self._get(f"/tv/{tv_id}/content_ratings", language=language) + + def tv_get_credits(self, tv_id: int, language: Optional[str] = None) -> Dict: + """ `TV Get Credits `__ + + Get the credits (cast and crew) that have been added to a TV show. + + Parameters: + tv_id (int): TV ID. + language (Optional[str]): ISO-639-1 or ISO-3166-1 value to display translated data for the fields that support it. + """ + return self._get(f"/tv/{tv_id}/credits", language=language) + + def tv_get_episode_groups(self, tv_id: int, language: Optional[str] = None) -> Dict: + """ `TV Get Episode Groups `__ + + Get all of the episode groups that have been created for a TV show. With a group ID you can call the :meth:`tv_episode_groups_get_details` method. + + Parameters: + tv_id (int): TV ID. + language (Optional[str]): ISO-639-1 or ISO-3166-1 value to display translated data for the fields that support it. + """ + return self._get(f"/tv/{tv_id}/episode_groups", language=language) + + def tv_get_external_ids(self, tv_id: int) -> Dict: + """ `TV Get External IDs `__ + + Get the external ids for a TV show. We currently support the following external sources. + + .. list-table:: + :header-rows: 1 + + * - Media Databases + - Social IDs + * - IMDb ID + - Facebook + * - TVDB ID + - Instagram + * - Freebase MID* + - Twitter + * - Freebase ID* + - + * - TVRage ID* + - + + \\* Defunct or no longer available as a service. + + Parameters: + tv_id (int): TV ID. + """ + return self._get(f"/tv/{tv_id}/external_ids") + + def tv_get_images( + self, tv_id: int, + language: Optional[str] = None, + include_image_language: Optional[str] = None + ) -> Dict: + """ `TV Get Images `__ + + Get the images that belong to a TV show. + + Querying images with a ``language`` parameter will filter the results. If you want to include a fallback + language (especially useful for backdrops) you can use the ``include_image_language`` parameter. This should be + a comma separated value like so: ``include_image_language=en,null``. + + Parameters: + tv_id (int): TV ID. + language (Optional[str]): ISO-639-1 or ISO-3166-1 value to display translated data for the fields that support it. + include_image_language (Optional[str]): Comma-separated list of ISO-639-1 values or null to query for additional image languages. + """ + return self._get(f"/tv/{tv_id}/images", language=language, include_image_language=include_image_language) + + def tv_get_keywords(self, tv_id: int) -> Dict: + """ `TV Get Keywords `__ + + Get the keywords that have been added to a TV show. + + Parameters: + tv_id (int): TV ID. + """ + return self._get(f"/tv/{tv_id}/keywords") + + def tv_get_recommendations(self, tv_id: int, language: Optional[str] = None, page: Optional[int] = None) -> Dict: + """ `TV Get Recommendations `__ + + Get the list of TV show recommendations for this item. + + Parameters: + tv_id (int): TV ID. + language (Optional[str]): ISO-639-1 or ISO-3166-1 value to display translated data for the fields that support it. + page (Optional[int]): Specify which page to query. + """ + return self._get(f"/tv/{tv_id}/recommendations", language=language, page=page) + + def tv_get_reviews(self, tv_id: int, language: Optional[str] = None, page: Optional[int] = None) -> Dict: + """ `TV Get Reviews `__ + + Get the reviews for a TV show. + + Parameters: + tv_id (int): TV ID. + language (Optional[str]): ISO-639-1 or ISO-3166-1 value to display translated data for the fields that support it. + page (Optional[int]): Specify which page to query. + """ + return self._get(f"/tv/{tv_id}/reviews", language=language, page=page) + + def tv_get_screened_theatrically(self, tv_id: int) -> Dict: + """ `TV Get Screened Theatrically `__ + + Get a list of seasons or episodes that have been screened in a film festival or theatre. + + Parameters: + tv_id (int): TV ID. + """ + return self._get(f"/tv/{tv_id}/screened_theatrically") + + def tv_get_similar_tv_shows(self, tv_id: int, language: Optional[str] = None, page: Optional[int] = None) -> Dict: + """ `TV Get Similar TV Shows `__ + + Get a list of similar TV shows. These items are assembled by looking at keywords and genres. + + Parameters: + tv_id (int): TV ID. + language (Optional[str]): ISO-639-1 or ISO-3166-1 value to display translated data for the fields that support it. + page (Optional[int]): Specify which page to query. + """ + return self._get(f"/tv/{tv_id}/similar", language=language, page=page) + + def tv_get_translations(self, tv_id: int) -> Dict: + """ `TV Get Translations `__ + + Get a list of the translations that exist for a TV show. + + Parameters: + tv_id (int): TV ID. + """ + return self._get(f"/tv/{tv_id}/translations") + + def tv_get_videos( + self, tv_id: int, + language: Optional[str] = None, + include_video_language: Optional[str] = None + ) -> Dict: + """ `TV Get Similar TV Shows `__ + + Get the videos that have been added to a TV show. + + Querying videos with a ``language`` parameter will filter the results. If you want to include a fallback + language you can use the ``include_video_language`` parameter. This should be a comma separated value like so: + ``include_video_language=en,null``. + + Parameters: + tv_id (int): TV ID. + language (Optional[str]): ISO-639-1 or ISO-3166-1 value to display translated data for the fields that support it. + include_video_language (Optional[str]): Comma-separated list of ISO-639-1 values or null to query for additional video languages. + """ + return self._get(f"/tv/{tv_id}/videos", language=language, include_video_language=include_video_language) + + def tv_get_watch_providers(self, tv_id: int) -> Dict: + """ `TV Get Watch Providers `__ + + Powered by our partnership with JustWatch, you can query this method to get a list of the availabilities per country by provider. + + This is not going to return full deep links, but rather, it's just enough information to display what's available where. + + You can link to the provided TMDB URL to help support TMDB and provide the actual deep links to the content. + + Please note: In order to use this data you must attribute the source of the data as JustWatch. If we find any usage not complying with these terms we will revoke access to the API. + + Parameters: + tv_id (int): TV ID. + """ + return self._get(f"/tv/{tv_id}/watch/providers") + + def tv_rate_tv_show( + self, tv_id: int, value: float, + session_id: Optional[str] = None, + guest_session_id: Optional[str] = None + ) -> Dict: + """ `TV Rate TV Show `__ + + Rate a TV show. + + Parameters: + tv_id (int): TV ID. + value (float): Rating for the TV show. + session_id (Optional[str]): Session ID. + guest_session_id (Optional[str]): Guest Session ID. + """ + return self._post( + f"/tv/{tv_id}/rating", + json={"value": value}, + session_id=session_id if session_id else self.session_id, + guest_session_id=guest_session_id + ) + + def tv_delete_rating( + self, tv_id: int, + session_id: Optional[str] = None, + guest_session_id: Optional[str] = None + ) -> Dict: + """ `TV Delete Rating `__ + + Remove your rating for a TV show. + + Parameters: + tv_id (int): TV ID. + session_id (Optional[str]): Session ID. + guest_session_id (Optional[str]): Guest Session ID. + """ + return self._delete( + f"/tv/{tv_id}/rating", + session_id=session_id if session_id else self.session_id, + guest_session_id=guest_session_id + ) + + def tv_get_latest(self, language: Optional[str] = None) -> Dict: + """ `TV Get Latest `__ + + Get the most newly created TV show. This is a live response and will continuously change. + + Parameters: + language (Optional[str]): ISO-639-1 or ISO-3166-1 value to display translated data for the fields that support it. + """ + return self._get("/tv/latest", language=language) + + def tv_get_tv_airing_today(self, language: Optional[str] = None, page: Optional[int] = None) -> Dict: + """ `TV Get TV Airing Today `__ + + Get a list of TV shows that are airing today. This query is purely day based as we do not currently support airing times. (Eastern Time UTC-05:00) + + Parameters: + language (Optional[str]): ISO-639-1 or ISO-3166-1 value to display translated data for the fields that support it. + page (Optional[int]): Specify which page to query. + """ + return self._get("/tv/airing_today", language=language, page=page) + + def tv_get_tv_on_the_air(self, language: Optional[str] = None, page: Optional[int] = None) -> Dict: + """ `TV Get TV On The Air `__ + + Get a list of shows that are currently on the air. + + This query looks for any TV show that has an episode with an air date in the next 7 days. + + Parameters: + language (Optional[str]): ISO-639-1 or ISO-3166-1 value to display translated data for the fields that support it. + page (Optional[int]): Specify which page to query. + """ + return self._get("/tv/on_the_air", language=language, page=page) + + def tv_get_popular(self, language: Optional[str] = None, page: Optional[int] = None) -> Dict: + """ `TV Get Popular `__ + + Get a list of the current popular TV shows on TMDB. This list updates daily. + + Parameters: + language (Optional[str]): ISO-639-1 or ISO-3166-1 value to display translated data for the fields that support it. + page (Optional[int]): Specify which page to query. + """ + return self._get("/tv/popular", language=language, page=page) + + def tv_get_top_rated(self, language: Optional[str] = None, page: Optional[int] = None) -> Dict: + """ `TV Get Top Rated `__ + + Get a list of the top rated TV shows on TMDB. + + Parameters: + language (Optional[str]): ISO-639-1 or ISO-3166-1 value to display translated data for the fields that support it. + page (Optional[int]): Specify which page to query. + """ + return self._get("/tv/top_rated", language=language, page=page) + + def tv_seasons_get_details( + self, tv_id: int, season_number: int, + language: Optional[str] = None, + append_to_response: Optional[str] = None, + session_id: Optional[str] = None, + guest_session_id: Optional[str] = None, + include_image_language: Optional[str] = None, + include_video_language: Optional[str] = None + ) -> Dict: + """ `TV Seasons Get Details `__ + + Get the TV season details by id.` + + Supports ``append_to_response``. Read more about this `here `__. + + Parameters: + tv_id (int): TV show ID. + season_number (int): Season Number of TV show. + language (Optional[str]): ISO-639-1 or ISO-3166-1 value to display translated data for the fields that support it. + append_to_response (Optional[str]): Comma-separated list of sub requests within the same namespace. + session_id (Optional[str]): Session ID. (Used with ``account_states`` append_to_response) + guest_session_id (Optional[str]): Guest Session ID. (Used with ``account_states`` append_to_response) + include_image_language (Optional[str]): Comma-separated list of ISO-639-1 values or null to query for additional image languages. (Used with ``images`` append_to_response) + include_video_language (Optional[str]): Comma-separated list of ISO-639-1 values or null to query for additional video languages. (Used with ``videos`` append_to_response) + """ + return self._get( + f"/tv/{tv_id}/season/{season_number}", + language=language, + append_to_response=append_to_response, + session_id=session_id if session_id else self.session_id, + guest_session_id=guest_session_id, + include_image_language=include_image_language, + include_video_language=include_video_language + ) + + def tv_seasons_get_account_states( + self, tv_id: int, season_number: int, + language: Optional[str] = None, + session_id: Optional[str] = None, + guest_session_id: Optional[str] = None + ) -> Dict: + """ `TV Seasons Get Account States `__ + + Returns all of the user ratings for the season's episodes. + + Parameters: + tv_id (int): TV show ID. + season_number (int): Season Number of TV show. + language (Optional[str]): ISO-639-1 or ISO-3166-1 value to display translated data for the fields that support it. + session_id (Optional[str]): Session ID. + guest_session_id (Optional[str]): Guest Session ID. + """ + return self._get( + f"/tv/{tv_id}/season/{season_number}/account_states", + language=language, + session_id=session_id if session_id else self.session_id, + guest_session_id=guest_session_id + ) + + def tv_seasons_get_aggregate_credits(self, tv_id: int, season_number: int, language: Optional[str] = None) -> Dict: + """ `TV Seasons Get Aggregate Credits `__ + + Get the aggregate credits for TV season. + + This call differs from the main ``credits`` call in that it does not only return the season credits, but rather is a view of all the cast & crew for all of the episodes belonging to a season. + + Parameters: + tv_id (int): TV show ID. + season_number (int): Season Number of TV show. + language (Optional[str]): ISO-639-1 or ISO-3166-1 value to display translated data for the fields that support it. + """ + return self._get(f"/tv/{tv_id}/season/{season_number}/aggregate_credits", language=language) + + def tv_seasons_get_changes( + self, season_id: int, + start_date: Optional[str] = None, + end_date: Optional[str] = None, + page: Optional[int] = None + ) -> Dict: + """ `TV Seasons Get Changes `__ + + Get the changes for a TV season. By default only the last 24 hours are returned. + + You can query up to 14 days in a single query by using the ``start_date`` and ``end_date`` query parameters. + + Parameters: + season_id (int): Season ID. + start_date (Optional[str]): Filter the results with a start date. Format: YYYY-MM-DD + end_date (Optional[str]): Filter the results with an end date. Format: YYYY-MM-DD + page (Optional[int]): Specify which page to query. + """ + return self._get( + f"/tv/season/{season_id}/changes", + start_date=start_date, + end_date=end_date, + page=page + ) + + def tv_seasons_get_credits(self, tv_id: int, season_number: int, language: Optional[str] = None) -> Dict: + """ `TV Seasons Get Credits `__ + + Get the credits for TV season. + + Parameters: + tv_id (int): TV show ID. + season_number (int): Season Number of TV show. + language (Optional[str]): ISO-639-1 or ISO-3166-1 value to display translated data for the fields that support it. + """ + return self._get(f"/tv/{tv_id}/season/{season_number}/credits", language=language) + + def tv_seasons_get_external_ids(self, tv_id: int, season_number: int) -> Dict: + """ `TV Seasons Get Credits `__ + + Get the external ids for a TV season. We currently support the following external sources. + + .. list-table:: + :header-rows: 1 + + * - Media Databases + * - TVDB ID + * - Freebase MID* + * - Freebase ID* + * - TVRage ID* + + \\* Defunct or no longer available as a service. + + Parameters: + tv_id (int): TV show ID. + season_number (int): Season Number of TV show. + """ + return self._get(f"/tv/{tv_id}/season/{season_number}/external_ids") + + def tv_seasons_get_images( + self, tv_id: int, season_number: int, + language: Optional[str] = None, + include_image_language: Optional[str] = None + ) -> Dict: + """ `TV Seasons Get Images `__ + + Get the images that belong to a TV season. + + Querying images with a ``language`` parameter will filter the results. If you want to include a fallback + language (especially useful for backdrops) you can use the ``include_image_language`` parameter. This should be + a comma separated value like so: ``include_image_language=en,null``. + + Parameters: + tv_id (int): TV ID. + season_number (int): Season Number of TV show. + language (Optional[str]): ISO-639-1 or ISO-3166-1 value to display translated data for the fields that support it. + include_image_language (Optional[str]): Comma-separated list of ISO-639-1 values or null to query for additional image languages. + """ + return self._get( + f"/tv/{tv_id}/season/{season_number}/images", + language=language, + include_image_language=include_image_language + ) + + def tv_seasons_get_translations(self, tv_id: int, season_number: int) -> Dict: + """ `TV Seasons Get Translations `__ + + Get the translation data for a TV season. + + Parameters: + tv_id (int): TV ID. + season_number (int): Season Number of TV show. + """ + return self._get(f"/tv/{tv_id}/season/{season_number}/translations") + + def tv_seasons_get_videos( + self, tv_id: int, season_number, + language: Optional[str] = None, + include_video_language: Optional[str] = None + ) -> Dict: + """ `TV Seasons Get Videos `__ + + Get the videos that have been added to a TV season. + + Querying videos with a ``language`` parameter will filter the results. If you want to include a fallback + language you can use the ``include_video_language`` parameter. This should be a comma separated value like so: + ``include_video_language=en,null``. + + Parameters: + tv_id (int): TV ID. + season_number (int): Season Number of TV show. + language (Optional[str]): ISO-639-1 or ISO-3166-1 value to display translated data for the fields that support it. + include_video_language (Optional[str]): Comma-separated list of ISO-639-1 values or null to query for additional video languages. + """ + return self._get( + f"/tv/{tv_id}/season/{season_number}/videos", + language=language, + include_video_language=include_video_language + ) + + def tv_episodes_get_details( + self, tv_id: int, season_number: int, episode_number: int, + language: Optional[str] = None, + append_to_response: Optional[str] = None, + session_id: Optional[str] = None, + guest_session_id: Optional[str] = None, + include_image_language: Optional[str] = None, + include_video_language: Optional[str] = None + ) -> Dict: + """ `TV Episodes Get Details `__ + + Get the TV episode details by id. + + Supports ``append_to_response``. Read more about this `here `__. + + Parameters: + tv_id (int): TV show ID. + season_number (int): Season Number of TV show. + episode_number (int): Episode Number of the Season in the TV show. + language (Optional[str]): ISO-639-1 or ISO-3166-1 value to display translated data for the fields that support it. + append_to_response (Optional[str]): Comma-separated list of sub requests within the same namespace. + session_id (Optional[str]): Session ID. (Used with ``account_states`` append_to_response) + guest_session_id (Optional[str]): Guest Session ID. (Used with ``account_states`` append_to_response) + include_image_language (Optional[str]): Comma-separated list of ISO-639-1 values or null to query for additional image languages. (Used with ``images`` append_to_response) + include_video_language (Optional[str]): Comma-separated list of ISO-639-1 values or null to query for additional video languages. (Used with ``videos`` append_to_response) + """ + return self._get( + f"/tv/{tv_id}/season/{season_number}/episode/{episode_number}", + language=language, + append_to_response=append_to_response, + session_id=session_id if session_id else self.session_id, + guest_session_id=guest_session_id, + include_image_language=include_image_language, + include_video_language=include_video_language + ) + + def tv_episodes_get_account_states( + self, tv_id: int, season_number: int, episode_number: int, + session_id: Optional[str] = None, + guest_session_id: Optional[str] = None + ) -> Dict: + """ `TV Episodes Get Account States `__ + + Get your rating for a episode. + + Parameters: + tv_id (int): TV show ID. + season_number (int): Season Number of TV show. + episode_number (int): Episode Number of the Season in the TV show. + session_id (Optional[str]): Session ID. + guest_session_id (Optional[str]): Guest Session ID. + """ + return self._get( + f"/tv/{tv_id}/season/{season_number}/episode/{episode_number}/account_states", + session_id=session_id if session_id else self.session_id, + guest_session_id=guest_session_id + ) + + def tv_episodes_get_changes( + self, episode_id, + start_date: Optional[str] = None, + end_date: Optional[str] = None, + page: Optional[int] = None + ) -> Dict: + """ `TV Episodes Get Changes `__ + + Get the changes for a TV episode. By default only the last 24 hours are returned. + + You can query up to 14 days in a single query by using the ``start_date`` and ``end_date`` query parameters. + + Parameters: + episode_id (int): Episode ID. + start_date (Optional[str]): Filter the results with a start date. Format: YYYY-MM-DD + end_date (Optional[str]): Filter the results with an end date. Format: YYYY-MM-DD + page (Optional[int]): Specify which page to query. + """ + return self._get( + f"/tv/episode/{episode_id}/changes", + start_date=start_date, + end_date=end_date, + page=page + ) + + def tv_episodes_get_credits( + self, tv_id: int, season_number: int, episode_number: int, + language: Optional[str] = None + ) -> Dict: + """ `TV Episodes Get Credits `__ + + Get the credits (cast, crew and guest stars) for a TV episode. + + Parameters: + tv_id (int): TV show ID. + season_number (int): Season Number of TV show. + episode_number (int): Episode Number of the Season in the TV show. + language (Optional[str]): ISO-639-1 or ISO-3166-1 value to display translated data for the fields that support it. + """ + return self._get(f"/tv/{tv_id}/season/{season_number}/episode/{episode_number}/credits", language=language) + + def tv_episodes_get_external_ids(self, tv_id: int, season_number: int, episode_number: int) -> Dict: + """ `TV Episodes Get External IDs `__ + + Get the external ids for a TV episode. We currently support the following external sources. + + .. list-table:: + :header-rows: 1 + + * - External Sources + * - IMDb ID + * - TVDB ID + * - Freebase MID* + * - Freebase ID* + * - TVRage ID* + + \\* Defunct or no longer available as a service. + + Parameters: + tv_id (int): TV show ID. + season_number (int): Season Number of TV show. + episode_number (int): Episode Number of the Season in the TV show. + """ + return self._get(f"/tv/{tv_id}/season/{season_number}/episode/{episode_number}/external_ids") + + def tv_episodes_get_images( + self, tv_id: int, season_number: int, episode_number: int, + language: Optional[str] = None, + include_image_language: Optional[str] = None + ) -> Dict: + """ `TV Episodes Get Images `__ + + Get the images that belong to a TV episode. + + Querying images with a ``language`` parameter will filter the results. If you want to include a fallback + language (especially useful for backdrops) you can use the ``include_image_language`` parameter. This should be + a comma separated value like so: ``include_image_language=en,null``. + + Parameters: + tv_id (int): TV show ID. + season_number (int): Season Number of TV show. + episode_number (int): Episode Number of the Season in the TV show. + language (Optional[str]): ISO-639-1 or ISO-3166-1 value to display translated data for the fields that support it. + include_image_language (Optional[str]): Comma-separated list of ISO-639-1 values or null to query for additional image languages. + """ + return self._get( + f"/tv/{tv_id}/season/{season_number}/episode/{episode_number}/images", + language=language, + include_image_language=include_image_language + ) + + def tv_episodes_get_translations(self, tv_id: int, season_number: int, episode_number: int) -> Dict: + """ `TV Episodes Get Translations `__ + + Get the translation data for an episode. + + Parameters: + tv_id (int): TV show ID. + season_number (int): Season Number of TV show. + episode_number (int): Episode Number of the Season in the TV show. + """ + return self._get(f"/tv/{tv_id}/season/{season_number}/episode/{episode_number}/translations") + + def tv_episodes_rate_tv_episode( + self, tv_id: int, season_number: int, episode_number: int, value: float, + session_id: Optional[str] = None, + guest_session_id: Optional[str] = None + ) -> Dict: + """ `TV Episodes Rate TV Episode `__ + + Rate a TV episode. + + Parameters: + tv_id (int): TV show ID. + season_number (int): Season Number of TV show. + episode_number (int): Episode Number of the Season in the TV show. + value (float): Rating for the TV show. + session_id (Optional[str]): Session ID. + guest_session_id (Optional[str]): Guest Session ID. + """ + return self._post( + f"/tv/{tv_id}/season/{season_number}/episode/{episode_number}/rating", + json={"value": value}, + session_id=session_id if session_id else self.session_id, + guest_session_id=guest_session_id + ) + + def tv_episodes_delete_rating( + self, tv_id: int, season_number: int, episode_number: int, + session_id: Optional[str] = None, + guest_session_id: Optional[str] = None + ) -> Dict: + """ `TV Episodes Delete Rating `__ + + Remove your rating for a TV episode. + + Parameters: + tv_id (int): TV show ID. + season_number (int): Season Number of TV show. + episode_number (int): Episode Number of the Season in the TV show. + session_id (Optional[str]): Session ID. + guest_session_id (Optional[str]): Guest Session ID. + """ + return self._delete( + f"/tv/{tv_id}/season/{season_number}/episode/{episode_number}/rating", + session_id=session_id if session_id else self.session_id, + guest_session_id=guest_session_id + ) + + def tv_episodes_get_videos( + self, tv_id: int, season_number: int, episode_number: int, + language: Optional[str] = None, + include_video_language: Optional[str] = None + ) -> Dict: + """ `TV Episodes Get Videos `__ + + Get the videos that have been added to a TV episode. + + Querying videos with a ``language`` parameter will filter the results. If you want to include a fallback + language you can use the ``include_video_language`` parameter. This should be a comma separated value like so: + ``include_video_language=en,null``. + + Parameters: + tv_id (int): TV show ID. + season_number (int): Season Number of TV show. + episode_number (int): Episode Number of the Season in the TV show. + language (Optional[str]): ISO-639-1 or ISO-3166-1 value to display translated data for the fields that support it. + include_video_language (Optional[str]): Comma-separated list of ISO-639-1 values or null to query for additional video languages. (Used with ``videos`` append_to_response) + """ + return self._get( + f"/tv/{tv_id}/season/{season_number}/episode/{episode_number}/videos", + language=language, + include_video_language=include_video_language + ) + + def tv_episode_groups_get_details(self, episode_group_id: str, language: Optional[str] = None) -> Dict: + """ `TV Episode Groups Get Details `__ + + Get the details of a TV episode group. Groups support 7 different types which are enumerated as the following: + + 1. Original air date + 2. Absolute + 3. DVD + 4. Digital + 5. Story arc + 6. Production + 7. TV + + Parameters: + episode_group_id (str): Episode Group ID. + language (Optional[str]): ISO-639-1 or ISO-3166-1 value to display translated data for the fields that support it. + """ + return self._get(f"/tv/episode_group/{episode_group_id}", language=language) + + def watch_providers_get_available_regions(self, language: Optional[str] = None) -> Dict: + """ `Watch Providers Get Available Regions `__ + + Returns a list of all of the countries we have watch provider (OTT/streaming) data for. + + Parameters: + language (Optional[str]): ISO-639-1 or ISO-3166-1 value to display translated data for the fields that support it. + """ + return self._get("/watch/providers/regions", language=language) + + def watch_providers_get_movie_providers(self, language: Optional[str] = None, watch_region: Optional[str] = None) -> Dict: + """ `Watch Providers Get Movie Providers `__ + + Returns a list of the watch provider (OTT/streaming) data we have available for movies. You can specify a ``watch_region`` param if you want to further filter the list by country. + + Parameters: + language (Optional[str]): ISO-639-1 or ISO-3166-1 value to display translated data for the fields that support it. + watch_region (Optional[str]): Use the ISO-3166-1 code to filter the providers that are available in a particular country. + """ + return self._get("/watch/providers/movie", language=language, watch_region=watch_region) + + def watch_providers_get_tv_providers(self, language: Optional[str] = None, watch_region: Optional[str] = None) -> Dict: + """ `Watch Providers Get TV Providers `__ + + Returns a list of the watch provider (OTT/streaming) data we have available for TV series. You can specify a ``watch_region`` param if you want to further filter the list by country. + + Parameters: + language (Optional[str]): ISO-639-1 or ISO-3166-1 value to display translated data for the fields that support it. + watch_region (Optional[str]): Use the ISO-3166-1 code to filter the providers that are available in a particular country. + """ + return self._get("/watch/providers/tv", language=language, watch_region=watch_region) diff --git a/tmdbapis/api4.py b/tmdbapis/api4.py new file mode 100644 index 0000000..2d221aa --- /dev/null +++ b/tmdbapis/api4.py @@ -0,0 +1,449 @@ +import logging +from json.decoder import JSONDecodeError +from typing import Dict, Optional, Union, List + +from requests import Session, Response +from requests.exceptions import RequestException + +from tmdbapis import util +from tmdbapis.exceptions import TMDbException, NotFound, Unauthorized, WritePermission, PrivateResource, Authentication + +logger = logging.getLogger(__name__) + +base_url = "https://api.themoviedb.org/4" + +class API4: + """ Raw V4 API Class containing all `TMDb API4 calls `__. + + Parameters: + access_token (str): TMDb V4 Access Token. + session (Optional[Session]): :class:`requests.Session` object. + validate (bool): Validate the TMDb V4 Access Token on creation. + + Attributes: + access_token (str): TMDb V4 Access Token. + has_write_token (bool): Does the provided TMDb V4 Access Token have write access. + account_id (str): TMDb V4 Account ID. + response (Response): TMDb V4 most recent full :class:`requests.Response` object. + """ + def __init__(self, access_token: str, session: Optional[Session] = None, validate: bool = True): + self.access_token = access_token + self._account_id = None + self._session = Session() if session is None else session + self.response = None + if validate: + try: + self.auth_create_access_token(self.access_token) + except TMDbException: + self.auth_create_request_token() + + def _get(self, path, **kwargs): + """ process get request. """ + return self._request("get", path, **kwargs) + + def _delete(self, path, json=None, **kwargs): + """ process delete request. """ + return self._request("delete", path, json=json, **kwargs) + + def _post(self, path, json=None, **kwargs): + """ process post request. """ + return self._request("post", path, json=json, **kwargs) + + def _put(self, path, json=None, **kwargs): + """ process put request. """ + return self._request("put", path, json=json, **kwargs) + + def _request(self, request_type, path, json=None, **kwargs): + """ process request. """ + url_params = {k: v for k, v in kwargs.items() if v is not None} + body_json = {k: v for k, v in json.items() if v is not None} if json else None + request_url = f"{base_url}{path}" + logger.debug(f"Request URL: {request_url}") + headers = {"Authorization": f"Bearer {self.access_token}"} + if body_json is not None: + logger.debug(f"Request JSON: {body_json}") + logger.debug(f"Headers: {headers}") + try: + if request_type == "delete": + self.response = self._session.delete(request_url, json=body_json, headers=headers, params=url_params) + elif request_type == "post": + self.response = self._session.post(request_url, json=body_json, headers=headers, params=url_params) + elif request_type == "put": + self.response = self._session.put(request_url, json=body_json, headers=headers, params=url_params) + else: + self.response = self._session.get(request_url, headers=headers, params=url_params) + response_json = self.response.json() + except (RequestException, JSONDecodeError): + raise TMDbException(f"Failed to Connect to {base_url}") + logger.debug(f"Response ({self.response.status_code} [{self.response.reason}]) {response_json}") + if self.response.status_code == 401: + if "status_code" in response_json: + if response_json["status_code"] == 36: + raise WritePermission("Requires V4 Authentication, use tmdbapis.v4_authenticate(), then approve the returned URL, and finally run tmdbapis.v4_approved()") + elif response_json["status_code"] == 39: + raise PrivateResource(response_json["status_message"]) + elif response_json["status_code"] == 7: + raise Unauthorized(response_json["status_message"]) + else: + raise TMDbException(f"({response_json['status_code']}) {response_json['status_message']}") + else: + raise TMDbException(f"({self.response.status_code} [{self.response.reason}]) {response_json}") + elif self.response.status_code == 404: + raise NotFound(f"({self.response.status_code} [{self.response.reason}]) Requested Item Not Found") + elif self.response.status_code >= 400: + raise TMDbException(f"({self.response.status_code} [{self.response.reason}]) {response_json}") + elif "errors" in response_json: + raise TMDbException(response_json["errors"]) + elif "success" in response_json and response_json["success"] is False: + raise TMDbException(response_json["status_message"]) + return response_json + + @property + def has_write_token(self): + return self._account_id is not None + + @property + def account_id(self): + if not self._account_id: + raise Authentication(f"Requires V4 API Write Access Token, use tmdbapis.v4_access_token(access_token)") + return self._account_id + + def account_get_lists(self, account_id: Optional[str] = None, page: Optional[int] = None) -> Dict: + """ `Account Get Lists `__ + + Get all of the lists you've created. + + Parameters: + account_id (Optional[str]): Account ID. + page (Optional[int]): Specify which page to query. + """ + return self._get(f"/account/{account_id if account_id else self.account_id}/lists", page=page) + + def account_get_favorite_movies( + self, account_id: Optional[str] = None, + sort_by: Optional[str] = None, + page: Optional[str] = None + ) -> Dict: + """ `Account Get Favorite Movies `__ + + Get the list of movies you have marked as a favorite. + + Parameters: + account_id (Optional[str]): Account ID. + sort_by (Optional[str]): Choose a sort option for the list of results. Allowed Values: ``created_at.asc``, ``created_at.desc``, ``release_date.asc``, ``release_date.desc``, ``title.asc``, ``title.desc``, ``vote_average.asc``, ``vote_average.desc`` + page (Optional[int]): Specify which page to query. + """ + return self._get(f"/account/{account_id if account_id else self.account_id}/movie/favorites", sort_by=sort_by, page=page) + + def account_get_favorite_tv_shows( + self, account_id: Optional[str] = None, + sort_by: Optional[str] = None, + page: Optional[str] = None + ) -> Dict: + """ `Account Get Favorite TV Shows `__ + + Get the list of TV shows you have marked as a favorite. + + Parameters: + account_id (Optional[str]): Account ID. + sort_by (Optional[str]): Choose a sort option for the list of results. Allowed Values: ``first_air_date.asc``, ``first_air_date.desc``, ``name.asc``, ``name.desc``, ``vote_average.asc``, ``vote_average.desc`` + page (Optional[int]): Specify which page to query. + """ + return self._get(f"/account/{account_id if account_id else self.account_id}/tv/favorites", sort_by=sort_by, page=page) + + def account_get_movie_recommendations( + self, account_id: Optional[str] = None, + sort_by: Optional[str] = None, + page: Optional[str] = None + ) -> Dict: + """ `Account Get Movie Recommendations `__ + + Get a list of your personal movie recommendations. + + Parameters: + account_id (Optional[str]): Account ID. + sort_by (Optional[str]): Choose a sort option for the list of results. Allowed Values: ``created_at.asc``, ``created_at.desc``, ``release_date.asc``, ``release_date.desc``, ``title.asc``, ``title.desc``, ``vote_average.asc``, ``vote_average.desc`` + page (Optional[int]): Specify which page to query. + """ + return self._get(f"/account/{account_id if account_id else self.account_id}/movie/recommendations", sort_by=sort_by, page=page) + + def account_get_tv_show_recommendations( + self, account_id: Optional[str] = None, + sort_by: Optional[str] = None, + page: Optional[str] = None + ) -> Dict: + """ `Account Get TV Show Recommendations `__ + + Get a list of your personal TV show recommendations. + + Parameters: + account_id (Optional[str]): Account ID. + sort_by (Optional[str]): Choose a sort option for the list of results. Allowed Values: ``first_air_date.asc``, ``first_air_date.desc``, ``name.asc``, ``name.desc``, ``vote_average.asc``, ``vote_average.desc`` + page (Optional[int]): Specify which page to query. + """ + return self._get(f"/account/{account_id if account_id else self.account_id}/tv/rated", sort_by=sort_by, page=page) + + def account_get_movie_watchlist( + self, account_id: Optional[str] = None, + sort_by: Optional[str] = None, + page: Optional[str] = None + ) -> Dict: + """ `Account Get Movie Watchlist `__ + + Get the list of movies you have added to your watchlist. + + Parameters: + account_id (Optional[str]): Account ID. + sort_by (Optional[str]): Choose a sort option for the list of results. Allowed Values: ``created_at.asc``, ``created_at.desc``, ``release_date.asc``, ``release_date.desc``, ``title.asc``, ``title.desc``, ``vote_average.asc``, ``vote_average.desc`` + page (Optional[int]): Specify which page to query. + """ + return self._get(f"/account/{account_id if account_id else self.account_id}/movie/watchlist", sort_by=sort_by, page=page) + + def account_get_tv_show_watchlist( + self, account_id: Optional[str] = None, + sort_by: Optional[str] = None, + page: Optional[str] = None + ) -> Dict: + """ `Account Get TV Show Watchlist `__ + + Get the list of TV shows you have added to your watchlist. + + Parameters: + account_id (Optional[str]): Account ID. + sort_by (Optional[str]): Choose a sort option for the list of results. Allowed Values: ``first_air_date.asc``, ``first_air_date.desc``, ``name.asc``, ``name.desc``, ``vote_average.asc``, ``vote_average.desc`` + page (Optional[int]): Specify which page to query. + """ + return self._get(f"/account/{account_id if account_id else self.account_id}/tv/watchlist", sort_by=sort_by, page=page) + + def account_get_rated_movies( + self, account_id: Optional[str] = None, + sort_by: Optional[str] = None, + page: Optional[str] = None + ) -> Dict: + """ `Account Get Rated Movies `__ + + Get the list of movies you have rated. + + Parameters: + account_id (Optional[str]): Account ID. + sort_by (Optional[str]): Choose a sort option for the list of results. Allowed Values: ``created_at.asc``, ``created_at.desc``, ``release_date.asc``, ``release_date.desc``, ``title.asc``, ``title.desc``, ``vote_average.asc``, ``vote_average.desc`` + page (Optional[int]): Specify which page to query. + """ + return self._get(f"/account/{account_id if account_id else self.account_id}/movie/rated", sort_by=sort_by, page=page) + + def account_get_rated_tv_shows( + self, account_id: Optional[str] = None, + sort_by: Optional[str] = None, + page: Optional[str] = None + ) -> Dict: + """ `Account Get Rated TV Shows `__ + + Get the list of TV shows you have rated. + + Parameters: + account_id (Optional[str]): Account ID. + sort_by (Optional[str]): Choose a sort option for the list of results. Allowed Values: ``first_air_date.asc``, ``first_air_date.desc``, ``name.asc``, ``name.desc``, ``vote_average.asc``, ``vote_average.desc`` + page (Optional[int]): Specify which page to query. + """ + return self._get(f"/account/{account_id if account_id else self.account_id}/tv/rated", sort_by=sort_by, page=page) + + def auth_create_request_token(self, redirect_to: Optional[str] = None) -> Dict: + """ `Auth Create Request Token `__ + + This method generates a new request token that you can ask a user to approve. This is the first step in + getting permission from a user to read and write data on their behalf. You can read more about this system + `here `__. + + Parameters: + redirect_to (Optional[str]): Redirect URL or callback that will be executed once a request token has been approved on TMDb. + """ + return self._post("/auth/request_token", json={"redirect_to": redirect_to} if redirect_to else None) + + def auth_create_access_token(self, request_token: str) -> Dict: + """ `Auth Create Access Token `__ + + This method will finish the user authentication flow and issue an official user access token. The request + token in this request is sent along as part of the POST body. You should still use your standard API read + access token for authenticating this request. + + Parameters: + request_token (str): Request Token + """ + response = self._post("/auth/access_token", json={"request_token": request_token}) + self.access_token = response["access_token"] + self._account_id = response["account_id"] + return response + + def auth_delete_access_token(self, access_token: str) -> Dict: + """ `Auth Delete Access Token `__ + + This method gives your users the ability to log out of a session. + + Parameters: + access_token (str): Access Token + """ + return self._delete("/auth/access_token", json={"access_token": access_token}) + + def list_get_list( + self, list_id: int, + language: Optional[str] = None, + sort_by: Optional[str] = None, + page: Optional[str] = None + ) -> Dict: + """ `List Get List `__ + + This method will retrieve a list by id. + + Private lists can only be accessed by their owners and therefore require a valid user access token. + + Parameters: + list_id (int): List ID + language (Optional[str]): ISO-639-1 or ISO-3166-1 value to display translated data for the fields that support it. + sort_by (Optional[str]): Choose a sort option for the list of results. Allowed Values: ``original_order.asc``, ``original_order.desc``, ``release_date.asc``, ``release_date.desc``, ``title.asc``, ``title.desc``, ``vote_average.asc``, ``vote_average.desc`` + page (Optional[int]): Specify which page to query. + """ + return self._get(f"/list/{list_id}", language=language, sort_by=sort_by, page=page) + + def list_create_list( + self, name: str, + iso_639_1: str, + description: Optional[str] = None, + public: Optional[bool] = None, + iso_3166_1: Optional[str] = None + ) -> Dict: + """ `List Create List `__ + + This method will create a new list. + + You will need to have valid user access token in order to create a new list. + + Parameters: + name (str): Set the name of your list. + iso_639_1 (str): Set the ISO-639-1 variant for your list. + description (Optional[str]): Set the description of your list. + public (Optional[bool]): Toggle the public status of your list. + iso_3166_1 (Optional[int]): Set the ISO-3166-1 variant for your list. + """ + return self._post( + "/list", + json={ + "name": name, + "iso_639_1": iso_639_1, + "description": description, + "public": public, + "iso_3166_1": iso_3166_1 + } + ) + + def list_update_list( + self, list_id: int, + name: Optional[str] = None, + description: Optional[str] = None, + public: Optional[bool] = None, + sort_by: Optional[str] = None + ) -> Dict: + """ `List Update List `__ + + This method will let you update the details of a list. + + You must be the owner of the list and therefore have a valid user access token in order to edit it. + + Parameters: + list_id (int): List ID + name (Optional[str]): Set the name of your list. + description (Optional[str]): Set the description of your list. + public (Optional[bool]): Toggle the public status of your list. + sort_by (Optional[str]): Choose a sort option for the list of results. Allowed Values: ``original_order.asc``, ``original_order.desc``, ``vote_average.asc``, ``vote_average.desc``, ``primary_release_date.asc``, ``primary_release_date.desc``, ``title.asc``, ``title.desc`` + """ + return self._put( + f"/list/{list_id}", + json={ + "name": name, + "description": description, + "public": public, + "sort_by": sort_by + } + ) + + def list_clear_list(self, list_id: int) -> Dict: + """ `List Clear List `__ + + This method lets you clear all of the items from a list in a single request. This action cannot be reversed so use it with caution. + + You must be the owner of the list and therefore have a valid user access token in order to clear a list. + + Parameters: + list_id (int): List ID + """ + return self._get(f"/list/{list_id}/clear") + + def list_delete_list(self, list_id: int) -> Dict: + """ `List Clear List `__ + + This method will delete a list by id. This action is not reversible so take care when issuing it. + + You must be the owner of the list and therefore have a valid user access token in order to delete it. + + Parameters: + list_id (int): List ID + """ + return self._delete(f"/list/{list_id}") + + def list_add_items(self, list_id: int, items: List[Dict[str, Union[str, int]]]) -> Dict: + """ `List Add Items `__ + + This method will let you add items to a list. We support essentially an unlimited number of items to be + posted at a time. Both movie and TV series are support. + + The results of this query will return a ``results`` array. Each result includes a ``success`` field. If a + result is ``false`` this will usually indicate that the item already exists on the list. It may also + indicate that the item could not be found. + + You must be the owner of the list and therefore have a valid user access token in order to add items to a + list. + + Parameters: + list_id (int): List ID + items (List[Dict[str, Union[str, int]]]): List of items to add. Each item is a dictionary with the format {"media_type": str, "media_id": int}. ``media_type`` can either be ``movie`` or ``tv``. + """ + return self._post(f"/list/{list_id}/items", json=util.validate_items(items)) + + def list_update_items(self, list_id: int, items: List[Dict[str, Union[str, int]]]) -> Dict: + """ `List Update Items `__ + + This method will let you update an individual item on a list. Currently, only adding a comment is supported. + + You must be the owner of the list and therefore have a valid user access token in order to edit items. + + Parameters: + list_id (int): List ID + items (List[Dict[str, Union[str, int]]]): List of items to update. Each item is a dictionary with the format {"media_type": str, "media_id": int, "comment": str}. ``media_type`` can either be ``movie`` or ``tv``. + """ + return self._put(f"/list/{list_id}/items", json=util.validate_items(items, comment=True)) + + def list_remove_items(self, list_id: int, items: List[Dict[str, Union[str, int]]]) -> Dict: + """ `List Remove Items `__ + + This method will let you remove items from a list. You can remove multiple items at a time. + + You must be the owner of the list and therefore have a valid user access token in order to delete items from it. + + Parameters: + list_id (int): List ID + items (List[Dict[str, Union[str, int]]]): List of items to remove. Each item is a dictionary with the format {"media_type": str, "media_id": int}. ``media_type`` can either be ``movie`` or ``tv``. + """ + return self._delete(f"/list/{list_id}/items", json=util.validate_items(items)) + + def list_check_item_status(self, list_id: int, media_id: int, media_type: str) -> Dict: + """ `List Check Item Status `__ + + This method lets you quickly check if the item is already added to the list. + + You must be the owner of the list and therefore have a valid user access token in order to check an item status. + + Parameters: + list_id (int): List ID + media_id (int): Media ID + media_type (str): Set the kind of media object are you checking. Allowed Values: ``movie``, ``tv`` + """ + return self._get(f"/list/{list_id}/item_status", media_id=media_id, media_type=media_type) diff --git a/tmdbapis/exceptions.py b/tmdbapis/exceptions.py new file mode 100644 index 0000000..91201d6 --- /dev/null +++ b/tmdbapis/exceptions.py @@ -0,0 +1,33 @@ +class TMDbException(Exception): + """ Base class for all TMDbAPIs exceptions. """ + pass + + +class Invalid(TMDbException): + """ Invalid Selection. """ + pass + + +class NotFound(TMDbException): + """ Item not found. """ + pass + + +class Unauthorized(TMDbException): + """ Invalid apikey. """ + pass + + +class Authentication(TMDbException): + """ Operation requires Authentication """ + pass + + +class PrivateResource(TMDbException): + """ Operation requires Private Resource """ + pass + + +class WritePermission(TMDbException): + """ Operation requires Write Permission """ + pass diff --git a/tmdbapis/objs/__init__.py b/tmdbapis/objs/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tmdbapis/objs/base.py b/tmdbapis/objs/base.py new file mode 100644 index 0000000..6ad1fec --- /dev/null +++ b/tmdbapis/objs/base.py @@ -0,0 +1,269 @@ +from abc import ABC, abstractmethod +from datetime import datetime +from typing import Any, Optional, Union, TYPE_CHECKING + +if TYPE_CHECKING: + from tmdbapis.tmdb import TMDbAPIs + from tmdbapis.api3 import API3 + from tmdbapis.api4 import API4 + + +class TMDbObj(ABC): + """ Base Class for TMDb Objects. """ + + def __init__(self, tmdb: "TMDbAPIs", data): + self._loading = True + self._partial = False + self._name = None + self._tmdb = tmdb + self._api: "API3" = tmdb._api if tmdb else None + self._api4: "API4" = tmdb._api4 if tmdb else None + self._load(data) + + @abstractmethod + def _load(self, data): + self._data = data + self._loading = True + self.id = None + + def _finish(self, name): + self._name = name + self._loading = False + + def _image_url(self, image_path): + return f"{self._tmdb._image_url}{image_path}" if image_path else None + + def __repr__(self): + return self.__str__() + + def __str__(self): + if self.id: + return f"[{self.id}:{self._name}]" + else: + return self._name + + def __eq__(self, other): + if type(self) is type(other): + if self.id is None and other.id is None: + return self._name == other._name + elif self.id is not None and other.id is not None: + return self.id == other.id + else: + return False + elif isinstance(other, int) and self.id is not None: + return self.id == other + else: + return str(self._name) == str(other) + + def __getattribute__(self, item): + value = super().__getattribute__(item) + if item.startswith("_") or self._loading or not self._partial or value is not None: + return value + self._load(None) + return super().__getattribute__(item) + + def __setattr__(self, key, value): + if key.startswith("_") or self._loading: + super().__setattr__(key, value) + else: + raise AttributeError("Attributes cannot be edited") + + def __delattr__(self, key): + raise AttributeError("Attributes cannot be deleted") + + def _parse(self, data=None, attrs: Optional[Union[str, list]] = None, value_type: str = "str", + default_is_none: bool = False, is_list: bool = False, is_dict: bool = False, extend: bool = False, + key: Any = None): + """ Validate the value given from the options given. + + Parameters: + attrs (Optional[Union[str, list]]): check data for these attributes. + value_type (str): Type that the value is. + default_is_none (bool): Makes default None. + is_list (bool): value is list of values. + is_dict (bool): value is dict of values. + extend (bool): value is list of values. + key (Any): extra key. + + Returns: + Any: Parsed Value + """ + + if default_is_none is False and value_type in ["int", "float"]: + default = 0 + elif default_is_none is False and is_list: + default = [] + else: + default = None + + value = self._data if data is None else data + if attrs: + if not isinstance(attrs, list): + attrs = [attrs] + for attr in attrs: + if attr in value: + value = value[attr] + else: + return default + + if value is None: + return default + elif extend: + export_list = [] + for v in value: + export_list.extend(self._parse(data=v, value_type=value_type, default_is_none=default_is_none, key=key)) + return export_list + elif is_list: + return [self._parse(data=v, value_type=value_type, default_is_none=default_is_none, key=key) for v in value] + elif is_dict: + return {k: self._parse(data=v, value_type=value_type, default_is_none=default_is_none, key=k) + for k, v in value.items()} + elif value_type == "int": + return int(value) + elif value_type == "float": + return float(value) + elif value_type == "bool": + if isinstance(value, bool): + return value + elif str(value).lower() in ["t", "true"]: + return True + elif str(value).lower() in ["f", "false"]: + return False + else: + return default + elif value_type == "date": + if not value: + return None + elif "T" in value: + return datetime.strptime(value[:-1].split(".")[0], "%Y-%m-%dT%H:%M:%S") + else: + return datetime.strptime(value, "%Y-%m-%d") + elif value_type == "dict": + return value + elif value_type == "alternative_name": + return tmdbapis.objs.simple.AlternativeName(self._tmdb, value) + elif value_type == "alternative_title": + return tmdbapis.objs.simple.AlternativeTitle(self._tmdb, value) + elif value_type == "certification": + return tmdbapis.objs.simple.Certification(self._tmdb, value) + elif value_type == "load_country": + return tmdbapis.objs.simple.Country(self._tmdb, value) + elif value_type == "country_certification": + return tmdbapis.objs.simple.CountryCertifications(self._tmdb, value, key) + elif value_type == "country_watch_provider": + return tmdbapis.objs.simple.CountryWatchProviders(self._tmdb, value, key) + elif value_type == "load_department": + return tmdbapis.objs.simple.Department(self._tmdb, value) + elif value_type == "load_genre": + return tmdbapis.objs.simple.Genre(self._tmdb, value) + elif value_type == "group": + return tmdbapis.objs.simple.Group(self._tmdb, value) + elif value_type == "load_language": + return tmdbapis.objs.simple.Language(self._tmdb, value) + elif value_type == "release_date": + return tmdbapis.objs.simple.ReleaseDate(self._tmdb, value) + elif value_type == "load_timezone": + return tmdbapis.objs.simple.Timezones(self._tmdb, value) + elif value_type == "trailer": + return tmdbapis.objs.simple.Trailer(self._tmdb, value) + elif value_type == "translation": + return tmdbapis.objs.simple.Translation(self._tmdb, value) + elif value_type == "user": + return tmdbapis.objs.simple.User(self._tmdb, value) + elif value_type == "video": + return tmdbapis.objs.simple.Video(self._tmdb, value) + elif value_type == "watch_provider": + return tmdbapis.objs.simple.WatchProvider(self._tmdb, value) + elif value_type == "backdrop": + return tmdbapis.objs.image.Backdrop(self._tmdb, value) + elif value_type == "logo": + return tmdbapis.objs.image.Logo(self._tmdb, value) + elif value_type == "poster": + return tmdbapis.objs.image.Poster(self._tmdb, value) + elif value_type == "profile": + return tmdbapis.objs.image.Profile(self._tmdb, value) + elif value_type == "still": + return tmdbapis.objs.image.Still(self._tmdb, value) + elif value_type == "tagged": + return tmdbapis.objs.image.Tagged(self._tmdb, value) + elif value_type == "collection": + return tmdbapis.objs.reload.Collection(self._tmdb, value) + elif value_type == "company": + return tmdbapis.objs.reload.Company(self._tmdb, value) + elif value_type == "movie_cast": + return tmdbapis.objs.reload.Credit(self._tmdb, value, credit_type="cast", media_type="movie") + elif value_type == "movie_crew": + return tmdbapis.objs.reload.Credit(self._tmdb, value, credit_type="crew", media_type="movie") + elif value_type == "tv_cast": + return tmdbapis.objs.reload.Credit(self._tmdb, value, credit_type="cast", media_type="tv") + elif value_type == "tv_crew": + return tmdbapis.objs.reload.Credit(self._tmdb, value, credit_type="crew", media_type="tv") + elif value_type == "agg_tv_cast": + cast = [] + for role in value["roles"]: + new_dict = value.copy() + for k, v in role.items(): + new_dict[k] = v + cast.append(tmdbapis.objs.reload.Credit(self._tmdb, new_dict, credit_type="cast", media_type="tv")) + return cast + elif value_type == "agg_tv_crew": + crew = [] + for role in value["jobs"]: + new_dict = value.copy() + for k, v in role.items(): + new_dict[k] = v + crew.append(tmdbapis.objs.reload.Credit(self._tmdb, new_dict, credit_type="crew", media_type="tv")) + return crew + elif value_type == "keyword": + return tmdbapis.objs.reload.Keyword(self._tmdb, value) + elif value_type == "movie": + return tmdbapis.objs.reload.Movie(self._tmdb, value) + elif value_type == "network": + return tmdbapis.objs.reload.Network(self._tmdb, value) + elif value_type == "person": + return tmdbapis.objs.reload.Person(self._tmdb, value) + elif value_type == "review": + return tmdbapis.objs.reload.Review(self._tmdb, value) + elif value_type == "tv": + return tmdbapis.objs.reload.TVShow(self._tmdb, value) + elif value_type == "season": + return tmdbapis.objs.reload.Season(self._tmdb, value, key) + elif value_type == "episode": + return tmdbapis.objs.reload.Episode(self._tmdb, value, key) + elif value_type == "episode_group": + return tmdbapis.objs.reload.EpisodeGroup(self._tmdb, value, key) + elif value_type == "media_type": + if value["media_type"] == "movie": + return tmdbapis.objs.reload.Movie(self._tmdb, value) + elif value["media_type"] == "tv": + return tmdbapis.objs.reload.TVShow(self._tmdb, value) + elif value["media_type"] == "person": + return tmdbapis.objs.reload.Person(self._tmdb, value) + elif value_type == "list": + return tmdbapis.objs.pagination.List(self._tmdb, value) + elif value_type == "movie_reviews": + return tmdbapis.objs.pagination.MovieReviews(self._tmdb, value, key) + elif value_type == "lists": + return tmdbapis.objs.pagination.MovieLists(self._tmdb, value, key) + elif value_type == "recommended_movies": + return tmdbapis.objs.pagination.RecommendedMovies(self._tmdb, value, key) + elif value_type == "similar_movies": + return tmdbapis.objs.pagination.SimilarMovies(self._tmdb, value, key) + elif value_type == "recommended_tv": + return tmdbapis.objs.pagination.RecommendedTVShows(self._tmdb, value, key) + elif value_type == "similar_tv": + return tmdbapis.objs.pagination.SimilarTVShows(self._tmdb, value, key) + elif value_type == "tagged_images": + return tmdbapis.objs.pagination.TaggedImages(self._tmdb, value, key) + elif value_type == "content_rating": + return {v["iso_3166_1"]: v["rating"] for v in value} + elif value_type in ["country", "language", "movie_genre", "tv_genre"]: + return self._tmdb._get_object(value, value_type) + else: + return str(value) + + +import tmdbapis.objs.simple +import tmdbapis.objs.image +import tmdbapis.objs.reload +import tmdbapis.objs.pagination diff --git a/tmdbapis/objs/image.py b/tmdbapis/objs/image.py new file mode 100644 index 0000000..d092b87 --- /dev/null +++ b/tmdbapis/objs/image.py @@ -0,0 +1,97 @@ +from tmdbapis.objs.base import TMDbObj + + +class TMDbImage(TMDbObj): + """ Represents a single Image. + + Attributes: + aspect_ratio (float): Image Aspect Ratio. + file_path (str): Image Path. + file_type (str): File type of the Image. + height (int): Image Height. + id (str): Image ID. + iso_639_1 (str): ISO 639-1 Language Code of the Image. + language (:class:`~tmdbapis.objs.simple.Language`): Language object for the ISO 639-1 Language Code. + url (str): Image Full URL. + width (int): Image Width. + vote_average (float): Image Vote Average. + vote_count (int): Image Vote Count. + """ + + def __init__(self, tmdb, data, image_type): + super().__init__(tmdb, data) + self._image_type = image_type + + def _load(self, data): + super()._load(data) + self.aspect_ratio = self._parse(attrs="aspect_ratio", value_type="float") + self.file_path = self._parse(attrs="file_path") + self.file_type = self._parse(attrs="file_type") + self.height = self._parse(attrs="height", value_type="int") + self.id = self._parse(attrs="id", value_type="str") + self.iso_639_1 = self._parse(attrs="iso_639_1") + self.language = self._tmdb._get_object(self._data, "language") + self.url = self._image_url(self.file_path) + self.vote_average = self._parse(attrs="vote_average", value_type="float") + self.vote_count = self._parse(attrs="vote_count", value_type="int") + self.width = self._parse(attrs="width", value_type="int") + self._finish(self.file_path) + + def __str__(self): + return f"[{self._image_type}:{self.file_path}]" + + +class Backdrop(TMDbImage): + """ Represents a single Backdrop Image. """ + + def __init__(self, tmdb, data): + super().__init__(tmdb, data, "Backdrop") + + +class Logo(TMDbImage): + """ Represents a single Logo Image. """ + + def __init__(self, tmdb, data): + super().__init__(tmdb, data, "Logo") + + +class Poster(TMDbImage): + """ Represents a single Poster Image. """ + + def __init__(self, tmdb, data): + super().__init__(tmdb, data, "Poster") + + +class Profile(TMDbImage): + """ Represents a single Profile Image. """ + + def __init__(self, tmdb, data): + super().__init__(tmdb, data, "Profile") + + +class Still(TMDbImage): + """ Represents a single Still Image. """ + + def __init__(self, tmdb, data): + super().__init__(tmdb, data, "Still") + + +class Tagged(TMDbImage): + """ Represents a single Tagged Image. + + Attributes: + image_type (str): Image Type. + media (Union[:class:`~tmdbapis.objs.reload.Movie`, :class:`~tmdbapis.objs.reload.TVShow`]): Media object associated with the Image. + media_type (str): Media Type for Image. + """ + + def __init__(self, tmdb, data): + super().__init__(tmdb, data, "Tagged") + + def _load(self, data): + super()._load(data) + self._loading = True + self.image_type = self._parse(attrs="image_type") + self.media_type = self._parse(attrs="media_type") + self.media = self._parse(attrs="media", value_type=self.media_type) + self._loading = False diff --git a/tmdbapis/objs/mixin.py b/tmdbapis/objs/mixin.py new file mode 100644 index 0000000..475b45f --- /dev/null +++ b/tmdbapis/objs/mixin.py @@ -0,0 +1,59 @@ +from abc import ABC, abstractmethod + +from tmdbapis.exceptions import Invalid +from tmdbapis.objs.base import TMDbObj + + +class Favorite(ABC): + def mark_as_favorite(self: TMDbObj): + """ Mark as a Favorite (Authentication Required) """ + self._api.account_mark_as_favorite(self._media_type(), self.id, True) + + def remove_as_favorite(self: TMDbObj): + """ Remove as a Favorite (Authentication Required) """ + self._api.account_mark_as_favorite(self._media_type(), self.id, False) + + @abstractmethod + def _media_type(self): + pass + + +class Rate(ABC): + def rate(self, rating: float): + """ Add a Rating (Authentication Required) + + Parameters: + rating (float): Rating to use. + """ + try: + if float(rating) < 0.5 or float(rating) > 10.0: + raise TypeError + except TypeError: + raise Invalid("Rating must be between 0.5 and 10.0") + self._rate(rating) + + def delete_rating(self): + """ Delete a Rating (Authentication Required) """ + self._delete_rate() + + @abstractmethod + def _rate(self, rating): + pass + + @abstractmethod + def _delete_rate(self): + pass + + +class Watchlist(ABC): + def add_to_watchlist(self: TMDbObj): + """ Add to your Watchlist (Authentication Required) """ + self._api.account_add_to_watchlist(self._media_type(), self.id, True) + + def remove_from_watchlist(self: TMDbObj): + """ Remove from your Watchlist (Authentication Required) """ + self._api.account_add_to_watchlist(self._media_type(), self.id, False) + + @abstractmethod + def _media_type(self): + pass diff --git a/tmdbapis/objs/pagination.py b/tmdbapis/objs/pagination.py new file mode 100644 index 0000000..5b54469 --- /dev/null +++ b/tmdbapis/objs/pagination.py @@ -0,0 +1,984 @@ +from abc import abstractmethod +from typing import Optional, Union, List, Tuple +from urllib.parse import urlparse, parse_qs + +from tmdbapis.objs.base import TMDbObj +from tmdbapis.objs.reload import Movie, TVShow +from tmdbapis import util +from tmdbapis.exceptions import NotFound, Invalid + + +class TMDbPagination(TMDbObj): + """ Represents a Pagination Object. The standard iterator only loops through the current page. + + Attributes: + page (int): Current Page. + results (int): Current Page's Results. + total_pages (int): Total Pages. + total_results (int): Total Results. + """ + def __init__(self, tmdb, data, value_type, page_type): + self._value_type = value_type + self._page_type = page_type + self._results_text = None + self._total_results_text = None + self._single_load = False + self._page_storage = {} + self._params = parse_qs(urlparse(tmdb._api.response.url).query) + super().__init__(tmdb=tmdb, data=data) + + def _load(self, data): + self._partial = data is not None + super()._load(self._get_page(1) if data is None else data) + self.page = 1 if self._single_load else self._parse(attrs="page", value_type="int") + self.total_pages = 1 if self._single_load else self._parse(attrs="total_pages", value_type="int") + if self._total_results_text: + self.total_results = self._parse(attrs=self._total_results_text, value_type="int") + setattr(self, self._total_results_text, self.total_results) + else: + self.total_results = self._parse(attrs="total_results", value_type="int") + if self._results_text: + self.results = self._parse(attrs=self._results_text, value_type=self._value_type, is_list=True) + setattr(self, self._results_text, self.results) + else: + self.results = self._parse(attrs="results", value_type=self._value_type, is_list=True) + self._page_storage[self.page] = self.results + self._finish(self._page_type) + + def load_next(self): + """ Loads the next page of the Paginated Object. + + Returns: + List[:class:`~tmdbapis.objs.pagination.Movie`]: List of Movies that changed. + + Raises: + :class:`~tmdbapis.exceptions.Invalid`: When ``start_date`` or ``end_date`` is in an incorrect format. + """ + if self.page + 1 > self.total_pages: + raise NotFound("No Next Page") + self.load_page(self.page + 1) + + def load_page(self, page: int): + """ Loads the page of the Paginated Object. + + Parameters: + page (int): page number to load. + + Raises: + :class:`~tmdbapis.exceptions.Invalid`: When ``page`` is not in the range of valid page numbers. + """ + page = int(page) + if page < 1 or page > self.total_pages: + raise Invalid(f"Page must be an integer 1-{self.total_pages}") + if page in self._page_storage: + self._loading = True + self.results = self._page_storage[page] + self.page = page + self._loading = False + else: + self._load(self._get_page(page)) + + def __iter__(self): + return (o for o in self.results) + + def __len__(self): + return self.total_results + + @abstractmethod + def _get_page(self, page): + pass + + def get_results(self, amount: int): + """ Gets the amount of results asked for from multiple pages. This method can make alot of calls to the API if you're not careful. + + Parameters: + amount (int): Amount of Items you want returned. + + Raises: + :class:`~tmdbapis.exceptions.Invalid`: When ``amount`` is not greater than zero. + """ + if int(amount) > self.total_results: + amount = self.total_results + if amount < 1: + raise Invalid("amount must be greater then 0") + results = [] + current_page = 0 + while len(results) < amount and current_page < self.total_pages: + current_page += 1 + self.load_page(current_page) + if len(results) + len(self.results) < amount: + results.extend(self.results) + else: + results.extend(self.results[:amount-len(results)]) + return results + + +class CreatedLists(TMDbPagination): + """ Paginated Object of the lists created by an account. Will include private lists if you are the owner. """ + def __init__(self, tmdb, v3=False): + self._v3 = v3 or tmdb._api4 is None + super().__init__(tmdb, None, "list", "CreatedLists") + + def _get_page(self, page): + if self._v3 or not self._api4.has_write_token: + return self._api.account_get_created_lists(language=self._tmdb.language, page=page) + else: + return self._api4.account_get_lists(page=page) + + +class DiscoverMovies(TMDbPagination): + """ Paginated Object of the Movie Discover search results. """ + def __init__(self, tmdb, **kwargs): + self._kwargs = kwargs + self._kwargs["language"] = tmdb.language + super().__init__(tmdb, None, "movie", "DiscoverMovies") + + def _get_page(self, page): + self._kwargs["page"] = page + return self._api.discover_movie_discover(**self._kwargs) + + +class DiscoverTVShows(TMDbPagination): + """ Paginated Object of the TV Discover search results. """ + def __init__(self, tmdb, **kwargs): + self._kwargs = kwargs + self._kwargs["language"] = tmdb.language + super().__init__(tmdb, None, "tv", "DiscoverTVShows") + + def _get_page(self, page): + self._kwargs["page"] = page + return self._api.discover_tv_discover(**self._kwargs) + + +class FavoriteMovies(TMDbPagination): + """ Paginated Object of your favorite movies. + + Attributes: + sort_by (str): How the paginated object is sorted. + """ + def __init__(self, tmdb, sort_by=None, v3=False): + self._v3 = v3 or tmdb._api4 is None + self._loading = True + self.sort_by = util.validate_sort(sort_by, self._v3, True) + self._loading = False + super().__init__(tmdb, None, "movie", "FavoriteMovies") + + def _get_page(self, page): + if self._v3 or not self._api4.has_write_token: + return self._api.account_get_favorite_movies(language=self._tmdb.language, sort_by=self.sort_by, page=page) + else: + return self._api4.account_get_favorite_movies(sort_by=self.sort_by, page=page) + + +class FavoriteTVShows(TMDbPagination): + """ Paginated Object of your favorite TV shows. + + Attributes: + sort_by (str): How the paginated object is sorted. + """ + def __init__(self, tmdb, sort_by=None, v3=False): + self._v3 = v3 or tmdb._api4 is None + self._loading = True + self.sort_by = util.validate_sort(sort_by, self._v3, False) + self._loading = False + super().__init__(tmdb, None, "tv", "FavoriteTVShows") + + def _get_page(self, page): + if self._v3 or not self._api4.has_write_token: + return self._api.account_get_favorite_tv_shows(language=self._tmdb.language, sort_by=self.sort_by, page=page) + else: + return self._api4.account_get_favorite_tv_shows(sort_by=self.sort_by, page=page) + + +class RatedEpisodes(TMDbPagination): + """ Paginated Object of the TV episodes you have rated. + + Attributes: + sort_by (str): How the paginated object is sorted. + """ + def __init__(self, tmdb, sort_by=None): + self._loading = True + self.sort_by = util.validate_sort(sort_by, True, False) + self._loading = False + super().__init__(tmdb, None, "episode", "RatedEpisodes") + + def _get_page(self, page): + return self._api.account_get_rated_tv_episodes(language=self._tmdb.language, sort_by=self.sort_by, page=page) + + +class RatedMovies(TMDbPagination): + """ Paginated Object of the movies you have rated. + + Attributes: + sort_by (str): How the paginated object is sorted. + """ + def __init__(self, tmdb, sort_by=None, v3=False): + self._v3 = v3 or tmdb._api4 is None + self._loading = True + self.sort_by = util.validate_sort(sort_by, self._v3, True) + self._loading = False + super().__init__(tmdb, None, "movie", "RatedMovies") + + def _get_page(self, page): + if self._v3 or not self._api4.has_write_token: + return self._api.account_get_rated_movies(language=self._tmdb.language, sort_by=self.sort_by, page=page) + else: + return self._api4.account_get_rated_movies(sort_by=self.sort_by, page=page) + + +class RatedTVShows(TMDbPagination): + """ Paginated Object of the TV shows you have rated. + + Attributes: + sort_by (str): How the paginated object is sorted. + """ + def __init__(self, tmdb, sort_by=None, v3=False): + self._v3 = v3 or tmdb._api4 is None + self._loading = True + self.sort_by = util.validate_sort(sort_by, self._v3, False) + self._loading = False + super().__init__(tmdb, None, "tv", "RatedTVShows") + + def _get_page(self, page): + if self._v3 or not self._api4.has_write_token: + return self._api.account_get_rated_tv_shows(language=self._tmdb.language, sort_by=self.sort_by, page=page) + else: + return self._api4.account_get_rated_tv_shows(sort_by=self.sort_by, page=page) + + +class MovieLists(TMDbPagination): + """ Paginated Object of the lists that this movie belongs to. + + Attributes: + movie_id (int): Movie ID referenced by this Paginated Object. + """ + def __init__(self, tmdb, data, movie_id): + self._loading = True + self.movie_id = movie_id + self._loading = False + super().__init__(tmdb, data, "list", "MovieLists") + + def _get_page(self, page): + return self._api.movies_get_lists(self.movie_id, language=self._tmdb.language, page=page) + + +class MovieRecommendations(TMDbPagination): + """ Paginated Object of your personal movie recommendations. + + Attributes: + sort_by (str): How the paginated object is sorted. + """ + def __init__(self, tmdb, sort_by=None): + self._loading = True + self.sort_by = util.validate_sort(sort_by, False, True) + self._loading = False + super().__init__(tmdb, None, "movie", "MovieRecommendations") + + def _get_page(self, page): + return self._tmdb._v4_check(write=True).account_get_movie_recommendations(sort_by=self.sort_by, page=page) + + +class MovieReviews(TMDbPagination): + """ Paginated Object of user reviews for a movie. + + Attributes: + movie_id (int): Movie ID referenced by this Paginated Object. + """ + def __init__(self, tmdb, data, movie_id): + self._loading = True + self.movie_id = movie_id + self._loading = False + super().__init__(tmdb, data, "review", "MovieReviews") + + def _get_page(self, page): + return self._api.movies_get_reviews(self.movie_id, language=self._tmdb.language, page=page) + + +class MovieWatchlist(TMDbPagination): + """ Paginated Object of the movies you have added to your watchlist. + + Attributes: + sort_by (str): How the paginated object is sorted. + """ + def __init__(self, tmdb, sort_by=None, v3=False): + self._v3 = v3 or tmdb._api4 is None + self._loading = True + self.sort_by = util.validate_sort(sort_by, self._v3, True) + self._loading = False + super().__init__(tmdb, None, "movie", "MovieWatchlist") + + def _get_page(self, page): + if self._v3 or not self._api4.has_write_token: + return self._api.account_get_movie_watchlist(language=self._tmdb.language, sort_by=self.sort_by, page=page) + else: + return self._api4.account_get_movie_watchlist(sort_by=self.sort_by, page=page) + + +class NowPlayingMovies(TMDbPagination): + """ Paginated Object of movies in theatres. + + Attributes: + region (str): ISO 3166-1 code used to filter release dates. + maximum_date (datetime): Maximum Date. + minimum_date (datetime): Minimum Date. + """ + def __init__(self, tmdb, region=None): + self._loading = True + self.region = region + self._loading = False + super().__init__(tmdb, None, "movie", "NowPlayingMovies") + + def _load(self, data): + super()._load(data) + self._loading = True + self.maximum_date = self._parse(attrs=["dates", "maximum"], value_type="date") + self.minimum_date = self._parse(attrs=["dates", "minimum"], value_type="date") + self._loading = False + + def _get_page(self, page): + return self._api.movies_get_now_playing(language=self._tmdb.language, region=self.region, page=page) + + +class PopularMovies(TMDbPagination): + """ Paginated Object of the current popular movies on TMDB. + + Attributes: + region (str): ISO 3166-1 code used to filter release dates. + """ + def __init__(self, tmdb, region=None): + self._loading = True + self.region = region + self._loading = False + super().__init__(tmdb, None, "movie", "PopularMovies") + + def _get_page(self, page): + return self._api.movies_get_popular(language=self._tmdb.language, region=self.region, page=page) + + +class PopularPeople(TMDbPagination): + """ Paginated Object of the list of popular people on TMDB. """ + def __init__(self, tmdb): + super().__init__(tmdb, None, "person", "PopularPerson") + + def _get_page(self, page): + return self._api.people_get_popular(self._tmdb.language, page=page) + + +class PopularTVShows(TMDbPagination): + """ Paginated Object of the current popular TV shows on TMDB. """ + def __init__(self, tmdb): + super().__init__(tmdb, None, "tv", "PopularTVShows") + + def _get_page(self, page): + return self._api.tv_get_popular(language=self._tmdb.language, page=page) + + +class RecommendedMovies(TMDbPagination): + """ Paginated Object of recommended movies for a movie. + + Attributes: + movie_id (int): Movie ID referenced by this Paginated Object. + """ + def __init__(self, tmdb, data, movie_id): + self._loading = True + self.movie_id = movie_id + self._loading = False + super().__init__(tmdb, data, "movie", "RecommendedMovies") + + def _get_page(self, page): + return self._api.movies_get_recommendations(self.movie_id, language=self._tmdb.language, page=page) + + +class RecommendedTVShows(TMDbPagination): + """ Paginated Object of recommended TV shows for a TV show. + + Attributes: + tv_id (int): TV ID referenced by this Paginated Object. + """ + def __init__(self, tmdb, data, tv_id): + self._loading = True + self.tv_id = tv_id + self._loading = False + super().__init__(tmdb, data, "tv", "RecommendedTVShows") + + def _get_page(self, page): + return self._api.tv_get_recommendations(self.tv_id, language=self._tmdb.language, page=page) + + +class SearchCollections(TMDbPagination): + """ Paginated Object of the Collections Search Results. + + Attributes: + query (str): Query of the search. + """ + def __init__(self, tmdb, query): + self._loading = True + self.query = query + self._loading = False + super().__init__(tmdb, None, "collection", "SearchCollections") + + def _get_page(self, page): + results = self._api.search_search_collections(self.query, language=self._tmdb.language, page=self.page) + if int(results["total_results"]) > 0: + return results + raise NotFound("No Results Found") + + +class SearchCompanies(TMDbPagination): + """ Paginated Object of the Companies Search Results. + + Attributes: + query (str): Query of the search. + """ + def __init__(self, tmdb, query): + self._loading = True + self.query = query + self._loading = False + super().__init__(tmdb, None, "company", "SearchCompanies") + + def _get_page(self, page): + results = self._api.search_search_companies(self.query, page=self.page) + if int(results["total_results"]) > 0: + return results + raise NotFound("No Results Found") + + +class SearchKeywords(TMDbPagination): + """ Paginated Object of the Keywords Search Results. + + Attributes: + query (str): Query of the search. + """ + def __init__(self, tmdb, query): + self._loading = True + self.query = query + self._loading = False + super().__init__(tmdb, None, "keyword", "SearchKeywords") + + def _get_page(self, page): + results = self._api.search_search_keywords(self.query, page=self.page) + if int(results["total_results"]) > 0: + return results + raise NotFound("No Results Found") + + +class SearchMovies(TMDbPagination): + """ Paginated Object of the Movies Search Results. + + Attributes: + query (str): Query of the search. + include_adult (bool): Adult results in the search. + region (str): ISO 3166-1 code used to filter the search. + year (int): Year used to filter the search. + primary_release_year (int): Primary Release Year used to filter the search. + """ + def __init__(self, tmdb, query, include_adult=None, region=None, year=None, primary_release_year=None): + self._loading = True + self.query = query + self.include_adult = include_adult + self.region = region + self.year = year + self.primary_release_year = primary_release_year + self._loading = False + super().__init__(tmdb, None, "movie", "SearchMovies") + + def _get_page(self, page): + results = self._api.search_search_movies( + self.query, + language=self._tmdb.language, + page=self.page, + include_adult=self.include_adult, + region=self.region, + year=self.year, + primary_release_year=self.primary_release_year + ) + if int(results["total_results"]) > 0: + return results + raise NotFound("No Results Found") + + +class SearchMulti(TMDbPagination): + """ Paginated Object of the Multi Search Results. + + Attributes: + query (str): Query of the search. + include_adult (bool): Adult results in the search. + region (str): ISO 3166-1 code used to filter the search. + """ + def __init__(self, tmdb, query, include_adult=None, region=None): + self._loading = True + self.query = query + self.include_adult = include_adult + self.region = region + self._loading = False + super().__init__(tmdb, None, "media_type", "SearchMulti") + + def _get_page(self, page): + results = self._api.search_multi_search( + self.query, + language=self._tmdb.language, + page=self.page, + include_adult=self.include_adult, + region=self.region + ) + if int(results["total_results"]) > 0: + return results + raise NotFound("No Results Found") + + +class SearchPeople(TMDbPagination): + """ Paginated Object of the People Search Results. + + Attributes: + query (str): Query of the search. + include_adult (bool): Adult results in the search. + region (str): ISO 3166-1 code used to filter the search. + """ + def __init__(self, tmdb, query, include_adult=None, region=None): + self._loading = True + self.query = query + self.include_adult = include_adult + self.region = region + self._loading = False + super().__init__(tmdb, None, "person", "SearchPeople") + + def _get_page(self, page): + results = self._api.search_search_people( + self.query, + language=self._tmdb.language, + page=self.page, + include_adult=self.include_adult, + region=self.region + ) + if int(results["total_results"]) > 0: + return results + raise NotFound("No Results Found") + + +class SearchTVShows(TMDbPagination): + """ Paginated Object of the TV Show Search Results. + + Attributes: + query (str): Query of the search. + include_adult (bool): Adult results in the search. + first_air_date_year (int): First Air Date Year used to filter the search. + """ + def __init__(self, tmdb, query, include_adult=None, first_air_date_year=None): + self._loading = True + self.query = query + self.include_adult = include_adult + self.first_air_date_year = first_air_date_year + self._loading = False + super().__init__(tmdb, None, "tv", "SearchTVShows") + + def _get_page(self, page): + results = self._api.search_search_tv_shows( + self.query, + language=self._tmdb.language, + page=self.page, + include_adult=self.include_adult, + first_air_date_year=self.first_air_date_year + ) + if int(results["total_results"]) > 0: + return results + raise NotFound("No Results Found") + + +class SimilarMovies(TMDbPagination): + """ Paginated Object of similar movies for a movie. + + Attributes: + movie_id (int): Movie ID referenced by this Paginated Object. + """ + def __init__(self, tmdb, data, movie_id): + self._loading = True + self.movie_id = movie_id + self._loading = False + super().__init__(tmdb, data, "movie", "SimilarMovies") + + def _get_page(self, page): + return self._api.movies_get_similar_movies(self.movie_id, language=self._tmdb.language, page=page) + + +class SimilarTVShows(TMDbPagination): + """ Paginated Object of similar TV shows for a TV show. + + Attributes: + tv_id (int): TV ID referenced by this Paginated Object. + """ + def __init__(self, tmdb, data, tv_id): + self._loading = True + self.tv_id = tv_id + self._loading = False + super().__init__(tmdb, data, "tv", "SimilarTVShows") + + def _get_page(self, page): + return self._api.tv_get_similar_tv_shows(self.tv_id, language=self._tmdb.language, page=page) + + +class TaggedImages(TMDbPagination): + """ Paginated Object of tagged images for a person. + + Attributes: + person_id (int): Person ID referenced by this Paginated Object. + """ + def __init__(self, tmdb, data, person_id): + self._loading = True + self.person_id = person_id + self._loading = False + super().__init__(tmdb, data, "tagged", "TaggedImages") + + def _get_page(self, page): + return self._api.people_get_tagged_images(self.person_id, language=self._tmdb.language, page=page) + + +class TMDbList(TMDbPagination): + """ Paginated Object of the items in a list. + + Attributes: + average_rating (float): Average Rating of the items in the List. (v4 Only) + backdrop_path (str): List backdrop path. (v4 Only) + backdrop_url (str): List full backdrop url. (v4 Only) + comments (Dict[str, str]): Dictionary of the comment for each item. (v4 Only) + country (:class:`~tmdbapis.objs.simple.Country`): Country object for the ISO 3166-1 Country Code. (v4 Only) + created_by (Union[str, :class:`~tmdbapis.objs.simple.User`]): User who created the list (str for v3 and :class:`~tmdbapis.objs.simple.User` for v4). + description (str): List Description. + favorite_count (int): Number of users who have marked this list as a favorite. (v3 Only) + id (int): List ID. + iso_3166_1 (str): ISO 3166-1 Alpha-2 Country Code of the List. (v4 Only) + iso_639_1 (str): ISO 639-1 Language Code of the List. + language (:class:`~tmdbapis.objs.simple.Language`): Language object for the ISO 639-1 Language Code. + name (int): List Name. + object_ids (Dict[str, str]): Dictionary of the object ids for each item. (v4 Only) + poster_path (str): List poster path. + poster_url (str): List full poster url. + public (bool): List Public. (v4 Only) + revenue (int): Total revenue of the items in the List. (v4 Only) + runtime (int): Total runtime of the items in the List. (v4 Only) + sort_by (str): How the List is sorted. (v4 Only) + """ + def __init__(self, tmdb, data, sort_by=None, load=False): + self._sort_by = sort_by + super().__init__(tmdb, data, "media_type", "List") + if load: + self._load(None) + + def _load(self, data): + self._partial = data is not None + if self._api4: + self._results_text = None + self._total_results_text = None + self._single_load = False + else: + self._results_text = "items" + self._total_results_text = "item_count" + self._single_load = True + super()._load(self._get_page(1) if data is None else data) + self._loading = True + self.description = self._parse(attrs="description") + self.id = self._parse(attrs="id", value_type="int") + self.iso_639_1 = self._parse(attrs="iso_639_1") + self.language = self._tmdb._get_object(self._data, "language") + self.name = self._parse(attrs="name") + self.poster_path = self._parse(attrs="poster_path") + self.poster_url = self._image_url(self.poster_path) + if self._api4: + self.average_rating = self._parse(attrs="average_rating", value_type="float") + self.backdrop_path = self._parse(attrs="backdrop_path") + self.backdrop_url = self._image_url(self.backdrop_path) + self.comments = self._parse(attrs="comments", value_type="dict") + self.country = self._tmdb._get_object(self._data, "country") + self.created_by = self._parse(attrs="created_by", value_type="user") + self.iso_3166_1 = self._parse(attrs="iso_3166_1") + self.object_ids = self._parse(attrs="object_ids", value_type="dict") + self.public = self._parse(attrs="public", value_type="bool") + self.revenue = self._parse(attrs="revenue", value_type="int") + self.runtime = self._parse(attrs="runtime", value_type="int") + self.sort_by = self._parse(attrs="sort_by") + else: + self.created_by = self._parse(attrs="created_by") + self.favorite_count = self._parse(attrs="favorite_count", value_type="int") + self._loading = False + + def _get_page(self, page): + if self._api4: + return self._api4.list_get_list( + self.id, + language=self._tmdb.language, + sort_by=self._sort_by, + page=page + ) + else: + return self._api.lists_get_details( + self.id, + language=self._tmdb.language + ) + + def reload(self): + """ Reloads the full object. """ + self._load(None) + + def update(self, name: Optional[str] = None, description: Optional[str] = None, + public: Optional[bool] = None, sort_by: Optional[str] = None): + """ Updates the list's metadata. + + Parameters: + name (Optional[str]): Updates the list name. + description (Optional[str]): Updates the list description. + public (Optional[bool]): Updates whether the list is public. + sort_by (Optional[str]): Updates the list sort_by. + + Raises: + :class:`~tmdbapis.exceptions.Invalid`: When no attribute is given or the ``sort_by`` given is invalid. + """ + if name is None and description is None and public is None and sort_by is None: + raise Invalid("Must have at least one parameter to update (name, description, public, or sort_by)") + if sort_by and sort_by not in util.v4_list_sorts: + raise Invalid(f"sort_by not in {util.v4_list_sorts}") + self._tmdb._v4_check().list_update_list( + self.id, + name=name, + description=description, + public=public, + sort_by=sort_by + ) + + def _check_item(self, item): + if isinstance(item, Movie): + item_id = item.id + item_type = "movie" + elif isinstance(item, TVShow): + item_id = item.id + item_type = "tv" + elif isinstance(item, tuple): + item_id = int(item[0]) + item_type = item[1] + if item_type not in ["movie", "tv"]: + raise Invalid("Tuple must have either 'movie' or 'tv'.") + else: + raise Invalid("Item must be either a Movie Object, a TV Show object, or a Tuple of the ID and either 'movie' or 'tv'.") + if item_type == "tv": + self._tmdb._v4_check() + return item_id, item_type + + def has_item(self, item: Union[Movie, TVShow, Tuple[int, str]]) -> bool: + """ Check to see if the Item given is in the List. + + Parameters: + item (Union[Movie, TVShow, Tuple[int, str]]): Item you want to check if it's on the list. If not using a Movie or Show object then you must use a Tuple with the ID and either ``movie`` or ``tv``. + + Raises: + :class:`~tmdbapis.exceptions.Invalid`: When the item given isn't in a valid format. + + Returns: + bool: Whether the list has the Item or not. + """ + item_id, item_type = self._check_item(item) + if self._api4 and self._api4.has_write_token: + try: + self._api4.list_check_item_status(self.id, item_id, item_type) + return True + except NotFound: + return False + else: + return self._api.lists_check_item_status(self.id, item_id)["item_present"] + + def add_items(self, items: List[Union[Movie, TVShow, Tuple[int, str]]]): + """ Adds the items given to the list. + + Parameters: + items (List[Union[Movie, TVShow, Tuple[int, str]]]): Items you want to add to the list. If not using a Movie or Show object then you must use a Tuple with the ID and either ``movie`` or ``tv``. + + Raises: + :class:`~tmdbapis.exceptions.Invalid`: When the item given isn't in a valid format. + + Returns: + bool: Whether the list has the Item or not. + """ + item_ids = [] + if not isinstance(items, list): + items = [items] + for item in items: + item_id, item_type = self._check_item(item) + item_ids.append({"media_type": item_type, "media_id": item_id}) + + if self._api4 and self._api4.has_write_token: + self._api4.list_add_items(self.id, item_ids) + else: + for item_id in item_ids: + self._api.lists_add_movie(self.id, item_id["media_id"]) + + def remove_items(self, items: List[Union[Movie, TVShow, Tuple[int, str]]]): + """ Adds the items given to the list. + + Parameters: + items (List[Union[Movie, TVShow, Tuple[int, str]]]): Item you want to remove from the list. If not using a Movie or Show object then you must use a Tuple with the ID and either ``movie`` or ``tv``. + + Raises: + :class:`~tmdbapis.exceptions.Invalid`: When the item given isn't in a valid format. + """ + item_ids = [] + if not isinstance(items, list): + items = [items] + for item in items: + item_id, item_type = self._check_item(item) + item_ids.append({"media_type": item_type, "media_id": item_id}) + + if self._api4 and self._api4.has_write_token: + self._api4.list_remove_items(self.id, item_ids) + else: + for item_id in item_ids: + self._api.lists_remove_movie(self.id, item_id["media_id"]) + + def update_items(self, items: List[Tuple[Union[Movie, TVShow, Tuple[int, str]], str]]): + """ Updates the items on the list. + + Parameters: + items (List[Tuple[Union[Movie, TVShow, Tuple[int, str]], str]]): Tuples of the items you want updated on the list and their description. If not using a Movie or Show object then you must use a Tuple of the ID and either ``movie`` or ``tv``. + + Raises: + :class:`~tmdbapis.exceptions.Invalid`: When the item given isn't in a valid format. + """ + item_ids = [] + if not isinstance(items, list): + items = [items] + for item in items: + item_id, item_type = self._check_item(item[0]) + comment = item[1] + item_ids.append({"media_type": item_type, "media_id": item_id, "comment": comment}) + self._tmdb._v4_check(write=True).list_update_items(self.id, item_ids) + + def clear(self): + """ Clear all items from the list. """ + if self._api4 and self._api4.has_write_token: + self._api4.list_clear_list(self.id) + else: + self._api.lists_clear_list(self.id, True) + + def delete(self): + """ Delete the list. """ + if self._api4 and self._api4.has_write_token: + self._api4.list_delete_list(self.id) + else: + self._api.lists_delete_list(self.id) + + +class TopRatedMovies(TMDbPagination): + """ Paginated Object of the top rated movies on TMDB. + + Attributes: + region (str): ISO 3166-1 code used to filter release dates. + """ + def __init__(self, tmdb, region=None): + self._loading = True + self.region = region + self._loading = False + super().__init__(tmdb, None, "movie", "TopRatedMovies") + + def _get_page(self, page): + return self._api.movies_get_top_rated(language=self._tmdb.language, region=self.region, page=page) + + +class TopRatedTVShows(TMDbPagination): + """ Paginated Object of the top rated TV shows on TMDB. """ + def __init__(self, tmdb): + super().__init__(tmdb, None, "tv", "TopRatedTVShows") + + def _get_page(self, page): + return self._api.tv_get_top_rated(language=self._tmdb.language, page=page) + + +class Trending(TMDbPagination): + """ Paginated Object of daily or weekly trending items. + + Attributes: + media_type (str): Media type of the Paginated Object. + time_window (str): Time Window for the Trending. + """ + def __init__(self, tmdb, media_type, time_window): + self._loading = True + self.media_type = media_type + self.time_window = time_window + self._loading = False + super().__init__(tmdb, None, "media_type", "Trending") + + def _get_page(self, page): + return self._api.trending_get_trending(self.media_type, self.time_window, page=page) + + +class TVShowRecommendations(TMDbPagination): + """ Paginated Object of your personal TV show recommendations. + + Attributes: + sort_by (str): How the paginated object is sorted. + """ + def __init__(self, tmdb, sort_by=None): + self._loading = True + self.sort_by = util.validate_sort(sort_by, False, False) + self._loading = False + super().__init__(tmdb, None, "tv", "TVShowRecommendations") + + def _get_page(self, page): + return self._tmdb._v4_check(write=True).account_get_tv_show_recommendations(sort_by=self.sort_by, page=page) + + +class TVShowsAiringToday(TMDbPagination): + """ Paginated Object of TV shows that are airing today. """ + def __init__(self, tmdb): + super().__init__(tmdb, None, "tv", "TopRatedMovies") + + def _get_page(self, page): + return self._api.tv_get_tv_airing_today(language=self._tmdb.language, page=page) + + +class TVShowsOnTheAir(TMDbPagination): + """ Paginated Object of shows that are currently on the air. """ + def __init__(self, tmdb): + super().__init__(tmdb, None, "tv", "TVShowsOnTheAir") + + def _get_page(self, page): + return self._api.tv_get_tv_on_the_air(language=self._tmdb.language, page=page) + + +class TVShowWatchlist(TMDbPagination): + """ Paginated Object of the TV shows you have added to your watchlist. + + Attributes: + sort_by (str): How the paginated object is sorted. + """ + def __init__(self, tmdb, sort_by=None, v3=False): + self._v3 = v3 or tmdb._api4 is None + self._loading = True + self.sort_by = util.validate_sort(sort_by, self._v3, False) + self._loading = False + super().__init__(tmdb, None, "tv", "TVShowWatchlist") + + def _get_page(self, page): + if self._v3 or not self._api4.has_write_token: + return self._api.account_get_tv_show_watchlist(language=self._tmdb.language, sort_by=self.sort_by, page=page) + else: + return self._api4.account_get_tv_show_watchlist(sort_by=self.sort_by, page=page) + + +class UpcomingMovies(TMDbPagination): + """ Paginated Object of upcoming movies in theatres. + + Attributes: + maximum_date (datetime): Maximum Date. + minimum_date (datetime): Minimum Date. + """ + def __init__(self, tmdb, region=None): + self._loading = True + self.region = region + self._loading = False + super().__init__(tmdb, None, "movie", "UpcomingMovies") + + def _load(self, data): + super()._load(data) + self._loading = True + self.maximum_date = self._parse(attrs=["dates", "maximum"], value_type="date") + self.minimum_date = self._parse(attrs=["dates", "minimum"], value_type="date") + self._loading = False + + def _get_page(self, page): + return self._api.movies_get_upcoming(language=self._tmdb.language, region=self.region, page=page) diff --git a/tmdbapis/objs/reload.py b/tmdbapis/objs/reload.py new file mode 100644 index 0000000..e8b5c9c --- /dev/null +++ b/tmdbapis/objs/reload.py @@ -0,0 +1,977 @@ +from abc import abstractmethod +from typing import Optional + +from tmdbapis.objs.base import TMDbObj +from tmdbapis.objs.mixin import Favorite, Rate, Watchlist + + +class TMDbReload(TMDbObj): + """ Base object for objects that Reload. """ + def __init__(self, tmdb, data, load=False): + super().__init__(tmdb, data) + if load: + self._load(None) + + @abstractmethod + def _load(self, data): + self._partial = data is not None + super()._load(self._full_load() if data is None else data) + + @abstractmethod + def _full_load(self): + pass + + def reload(self): + """ Reloads the full object. """ + self._load(None) + + +class Account(TMDbReload): + """ Represents a single User Account. + + Attributes: + avatar_hash (str): Avatar Hash Value. + avatar_path (str): Avatar Path. + avatar_url (str): Avatar Full URL. + country (:class:`~tmdbapis.objs.simple.Country`): Country object for the ISO 3166-1 Country Code. + id (str): v3 User Account ID. + include_adult (bool): Default include adult items in search results + iso_3166_1 (str): Default ISO 3166-1 Alpha-2 Country Code of the User Account. + iso_639_1 (str): Default ISO 639-1 Language Code of the User Account. + language (:class:`~tmdbapis.objs.simple.Language`): Language object for the ISO 639-1 Language Code. + name (str): User Account Name. + username (str): User Account Username. + """ + + def __init__(self, tmdb): + super().__init__(tmdb, None) + + def _load(self, data): + super()._load(None) + self.avatar_hash = self._parse(attrs=["avatar", "gravatar", "hash"]) + self.avatar_path = self._parse(attrs=["avatar", "tmdb", "avatar_path"]) + self.avatar_url = self._image_url(self.avatar_path) + self.country = self._tmdb._get_object(self._data, "country") + self.id = self._parse(attrs="id", value_type="int") + self.include_adult = self._parse(attrs="include_adult") + self.iso_3166_1 = self._parse(attrs="iso_3166_1") + self.iso_639_1 = self._parse(attrs="iso_639_1") + self.language = self._tmdb._get_object(self._data, "language") + self.name = self._parse(attrs="name") + self.username = self._parse(attrs="username") + self._finish(self.name) + + def _full_load(self): + return self._api.account_get_details() + + def created_lists(self, v3: bool = False): + """ Alias for :meth:`~.tmdb.TMDbAPIs.created_lists` """ + return self._tmdb.created_lists(v3=v3) + + def favorite_movies(self, sort_by: str = None, v3: bool = False): + """ Alias for :meth:`~.tmdb.TMDbAPIs.favorite_movies` """ + return self._tmdb.favorite_movies(sort_by=sort_by, v3=v3) + + def favorite_tv_shows(self, sort_by: Optional[str] = None, v3: bool = False): + """ Alias for :meth:`~.tmdb.TMDbAPIs.favorite_tv_shows` """ + return self._tmdb.favorite_tv_shows(sort_by=sort_by, v3=v3) + + def movie_recommendations(self, sort_by: Optional[str] = None): + """ Alias for :meth:`~.tmdb.TMDbAPIs.movie_recommendations` """ + return self._tmdb.movie_recommendations(sort_by=sort_by) + + def movie_watchlist(self, sort_by: Optional[str] = None, v3: bool = False): + """ Alias for :meth:`~.tmdb.TMDbAPIs.movie_watchlist` """ + return self._tmdb.movie_watchlist(sort_by=sort_by, v3=v3) + + def rated_episodes(self, sort_by: Optional[str] = None): + """ Alias for :meth:`~.tmdb.TMDbAPIs.rated_episodes` """ + return self._tmdb.rated_episodes(sort_by=sort_by) + + def rated_movies(self, sort_by: Optional[str] = None, v3: bool = False): + """ Alias for :meth:`~.tmdb.TMDbAPIs.rated_movies` """ + return self._tmdb.rated_movies(sort_by=sort_by, v3=v3) + + def rated_tv_shows(self, sort_by: Optional[str] = None, v3: bool = False): + """ Alias for :meth:`~.tmdb.TMDbAPIs.rated_tv_shows` """ + return self._tmdb.rated_tv_shows(sort_by=sort_by, v3=v3) + + def tv_show_recommendations(self, sort_by: Optional[str] = None): + """ Alias for :meth:`~.tmdb.TMDbAPIs.tv_show_recommendations` """ + return self._tmdb.tv_show_recommendations(sort_by=sort_by) + + def tv_show_watchlist(self, sort_by: Optional[str] = None, v3: bool = False): + """ Alias for :meth:`~.tmdb.TMDbAPIs.tv_show_watchlist` """ + return self._tmdb.tv_show_watchlist(sort_by=sort_by, v3=v3) + + +class Collection(TMDbReload): + """ Represents a single Collection. + + Attributes: + backdrop_path (str): Backdrop Path. + backdrop_url (str): Backdrop Full URL. + backdrops (List[:class:`~tmdbapis.objs.image.Backdrop`]): List of other Backdrops for the Collection. + id (int): Collection ID. + movies (List[:class:`~tmdbapis.objs.reload.Movie`]): Movies within the Collection. + name (str): Collection Name. + overview (str): Collection Overview. + poster_path (str): Poster Path. + poster_url (str): Poster Full URL. + posters (List[:class:`~tmdbapis.objs.image.Poster`]): List of other Posters for the Collection. + translations (List[:class:`~tmdbapis.objs.simple.Translation`]): List of Translations for the Collection. + """ + + def _load(self, data): + super()._load(data) + self.backdrop_path = self._parse(attrs="backdrop_path") + self.backdrop_url = self._image_url(self.backdrop_path) + self.backdrops = self._parse(attrs=["images", "backdrops"], value_type="backdrop", is_list=True) + self.id = self._parse(attrs="id", value_type="int") + self.movies = self._parse(attrs="parts", value_type="movie", is_list=True) + self.name = self._parse(attrs="name") + self.overview = self._parse(attrs="overview") + self.poster_path = self._parse(attrs="poster_path") + self.poster_url = self._image_url(self.poster_path) + self.posters = self._parse(attrs=["images", "posters"], value_type="poster", is_list=True) + self.translations = self._parse(attrs=["translations", "translations"], value_type="translation", is_list=True) + self._finish(self.name) + + def _full_load(self): + return self._api.collections_get_details( + self.id, + language=self._tmdb.language, + append_to_response="images,translations", + include_image_language=self._tmdb._include_language + ) + + +class Company(TMDbReload): + """ Represents a single Company. + + Attributes: + alternative_names (List[:class:`~tmdbapis.objs.simple.AlternativeName`]): Company Alternative Names. + description (str): Company Description. + headquarters (str): Company Headquarters. + homepage (str): Company Homepage. + id (int): Company ID. + logo_path (str): Logo Path. + logo_url (str): Logo Full URL. + logos (List[:class:`~tmdbapis.objs.image.Logo`]): List of other Logos for the Company. + name (str): Company Name. + movies (:class:`~tmdbapis.objs.pagination.DiscoverMovies`): Pagination Object of Company Movies. + origin_country (str): Company Origin Country. + parent_company (:class:`~tmdbapis.objs.reload.Company`): Parent Company. + tv_shows (:class:`~tmdbapis.objs.pagination.DiscoverTVShows`): Pagination Object of Company TV Shows. + """ + + def _load(self, data): + super()._load(data) + self._movies = None + self._tv_shows = None + self.alternative_names = self._parse(attrs=["alternative_names", "results"], + value_type="alternative_name", is_list=True) + self.description = self._parse(attrs="description") + self.headquarters = self._parse(attrs="headquarters") + self.homepage = self._parse(attrs="homepage") + self.id = self._parse(attrs="id", value_type="int") + self.logo_path = self._parse(attrs="logo_path") + self.logo_url = self._image_url(self.logo_path) + self.logos = self._parse(attrs=["images", "logos"], value_type="logo", is_list=True) + self.name = self._parse(attrs="name") + self.origin_country = self._parse(attrs="origin_country") + self.parent_company = self._parse(attrs="parent_company", value_type="company") + self._finish(self.name) + + def _full_load(self): + return self._api.companies_get_details( + self.id, + language=self._tmdb.language, + append_to_response="alternative_names,images" + ) + + @property + def movies(self): + if not self._movies: + self._movies = self._tmdb.discover_movies(with_companies=self.id) + return self._movies + + @property + def tv_shows(self): + if not self._tv_shows: + self._tv_shows = self._tmdb.discover_tv_shows(with_companies=self.id) + return self._tv_shows + + +class Configuration(TMDbReload): + """ Represents TMDb's Configuration. + + Attributes: + backdrop_sizes (List[str]): Backdrop sizes. + base_image_url (str): Base Image URL. + change_keys (List[str]): Change Keys. + countries (List[:class:`~tmdbapis.objs.simple.Country`]): Countries in TMDb. + departments (List[:class:`~tmdbapis.objs.simple.Department`]): Departments in TMDb. + languages (List[:class:`~tmdbapis.objs.simple.Language`]): Languages in TMDb. + logo_sizes (List[str]): Logo Sizes. + poster_sizes (List[str]): Poster Sizes. + primary_translations (List[str]): Primary Translations in TMDb. + profile_sizes (List[str]): Profile Sizes. + secure_base_image_url (str): Secure Base Image URL. + still_sizes (List[str]): Still Sizes + timezones (List[str]): Timezones in TMDb. + """ + + def __init__(self, tmdb): + super().__init__(tmdb, None) + + def _load(self, data): + super()._load(data) + self.backdrop_sizes = self._parse(attrs=["images", "backdrop_sizes"], is_list=True) + self.base_image_url = self._parse(attrs=["images", "base_url"]) + self.change_keys = self._parse(attrs="change_keys", is_list=True) + self.countries = self._parse(attrs="countries", value_type="load_country", is_list=True) + self.departments = self._parse(attrs="jobs", value_type="load_department", is_list=True) + self.languages = self._parse(attrs="languages", value_type="load_language", is_list=True) + self.logo_sizes = self._parse(attrs=["images", "logo_sizes"], is_list=True) + self.poster_sizes = self._parse(attrs=["images", "poster_sizes"], is_list=True) + self.primary_translations = self._parse(attrs="primary_translations", is_list=True) + self.profile_sizes = self._parse(attrs=["images", "profile_sizes"], is_list=True) + self.secure_base_image_url = self._parse(attrs=["images", "secure_base_url"]) + self.still_sizes = self._parse(attrs=["images", "still_sizes"], is_list=True) + self.timezones = self._parse(attrs="timezones", value_type="load_timezone", is_list=True) + self._finish("API3 Configuration") + + def _full_load(self): + return self._api.configuration_get_api_configuration( + append_to_response="countries,jobs,languages,primary_translations,timezones" + ) + + +class Credit(TMDbReload): + """ Represents a single Credit. + + Attributes: + adult (bool): Is the Actor an adult actor. + character (str): Character Name. + credit_type (str): Credit Type. + department (str): Credit Department + episode_count (int): Number of Episodes the Actor appeared in. + episodes (List[:class:`~tmdbapis.objs.reload.Episode`]): Episodes of credit. Only exists when media_type == "tv". + gender (int): Actor's Gender. (1: Women, 2: Men) + id (str): The Credit ID. + job (str): Job in Department. + known_for (List[Union[:class:`~tmdbapis.objs.reload.Movie`, :class:`~tmdbapis.objs.reload.TVShow`]]): + known_for_department (str): Department Actor is known for. + media_type (str): Media Type of the Credit. + movie (:class:`~tmdbapis.objs.reload.Movie`): Movie of credit. Only exists when media_type == "movie" . + name (str): Actor Name. + order (int): Order of the Credits. + original_name (str): Actor Original Name. + person_id (int): Person ID of the Actor. + popularity (float): Popularity of the Credit. + profile_path (str): Profile Path. + profile_url (str): Profile Full URL. + seasons (List[:class:`~tmdbapis.objs.reload.Season`]): Season of credit. Only exists when media_type == "tv". + tv_show (:class:`~tmdbapis.objs.reload.TVShow`): TV Show of credit. Only exists when media_type == "tv". + """ + + def __init__(self, tmdb, data, credit_type=None, media_type=None, load=False): + self._credit_type = credit_type + self._media_type = media_type + super().__init__(tmdb, data=data, load=load) + + def _load(self, data): + super()._load(data) + + def dict_check(dict_attr, attr): + return [dict_attr, attr] if dict_attr in self._data else attr + + self.adult = self._parse(attrs=dict_check("person", "adult"), value_type="bool") + self.character = self._parse(attrs=dict_check("media", "character")) + self.credit_type = self._parse(attrs="credit_type") + if not self.credit_type: + self.credit_type = self._credit_type + self.department = self._parse(attrs="department") + if "episode_count" in self._data: + self.episode_count = self._parse(attrs="episode_count", value_type="int") + self.gender = self._parse(attrs=dict_check("person", "gender"), value_type="int") + self.id = self._parse(attrs="credit_id" if "credit_id" in self._data else "id") + self.job = self._parse(attrs="job") + self.known_for = self._parse(attrs=["person", "known_for"], value_type="media_type", is_list=True) + self.known_for_department = self._parse(attrs=dict_check("person", "known_for_department")) + self.media_type = self._parse(attrs="media_type") + if not self.media_type: + self.media_type = self._media_type + self.name = self._parse(attrs=dict_check("person", "name")) + self.order = self._parse(attrs="order", value_type="int") + self.original_name = self._parse(attrs="original_name") + self.person_id = self._parse(attrs=dict_check("person", "id"), value_type="int") + self.popularity = self._parse(attrs=dict_check("person", "popularity"), value_type="int") + self.profile_path = self._parse(attrs=dict_check("person", "profile_path")) + self.profile_url = self._image_url(self.profile_path) + if self.media_type == "movie": + self.movie = self._parse(attrs="media", value_type="movie") + elif self.media_type == "tv": + self.tv_show = self._parse(attrs="media", value_type="tv") + self.seasons = self._parse(attrs=["media", "seasons"], value_type="season", is_list=True) + self.episodes = self._parse(attrs=["media", "episodes"], value_type="episode", is_list=True) + + def _full_load(self): + return self._api.credits_get_details(self.id) + + +class Episode(TMDbReload, Rate): + """ Represents a single Episode. + + Attributes: + air_date (datetime): Episode Air Date. + cast (List[:class:`~tmdbapis.objs.reload.Credit`]): List of Episode Cast Credits. + crew (List[:class:`~tmdbapis.objs.reload.Credit`]): List of Episode Crew Credits. + episode_number (int): Episode in Season Number. + freebase_id (str): Freebase ID for the Episode. + freebase_mid (str): Freebase MID for the Episode. + guest_stars (List[:class:`~tmdbapis.objs.reload.Credit`]): List of Episode Guest Stars Credits. + id (int): Episode ID. + imdb_id (str): Episode IMDb ID. + name (str): Episode Name. + order (int): Episode Order in Group. + overview (str): Episode Overview. + production_code (str): Episode Production Code. + rated (Union[float, bool]): Either your TMDb Rating for the Episode or False if there is no rating. + season_number (int): Season the Episode is in. + still_path (str): Still Path. + still_url (str): Still Full URL. + stills (List[:class:`~tmdbapis.objs.image.Still`]): List of other Stills for the Episode. + translations (List[:class:`~tmdbapis.objs.simple.Translation`]): List of Translations for the Episode. + tv_id (int): TMDb TV Show ID the contains the Episode. + tvdb_id (int): TVDB ID of the Episode. + tvrage_id (int): TVRage ID of the Episode. + videos (List[:class:`~tmdbapis.objs.simple.Video`]): List of Videos associated with the Episode. + vote_average (float): Vote Average for the Episode. + vote_count (int): Number of Votes for the Episode. + """ + + def __init__(self, tmdb, data, tv_id=None, load=False): + self._tv_id = tv_id + super().__init__(tmdb, data=data, load=load) + + def _load(self, data): + super()._load(data) + self.air_date = self._parse(attrs="air_date", value_type="date") + self.cast = self._parse(attrs=["credits", "cast"], value_type="tv_cast", is_list=True) + self.crew = self._parse(attrs="crew", value_type="tv_crew", is_list=True) + self.episode_number = self._parse(attrs="episode_number", value_type="int") + self.freebase_id = self._parse(attrs=["external_ids", "freebase_id"]) + self.freebase_mid = self._parse(attrs=["external_ids", "freebase_mid"]) + self.guest_stars = self._parse(attrs="guest_stars", value_type="tv_cast", is_list=True) + self.id = self._parse(attrs="id", value_type="int") + self.imdb_id = self._parse(attrs=["external_ids", "imdb_id"]) + self.name = self._parse(attrs="name") + self.order = self._parse(attrs="order", value_type="int") if "order" in self._data else None + self.overview = self._parse(attrs="overview") + self.production_code = self._parse(attrs="production_code") + try: + self.rated = self._parse(attrs=["account_states", "rated"], value_type="float") + except ValueError: + self.rated = self._parse(attrs=["account_states", "rated"], value_type="bool") + self.season_number = self._parse(attrs="season_number", value_type="int") + self.still_path = self._parse(attrs="still_path") + self.still_url = self._image_url(self.still_path) + self.stills = self._parse(attrs=["images", "stills"], value_type="still", is_list=True) + self.translations = self._parse(attrs=["translations", "translations"], value_type="translation", is_list=True) + self.tv_id = self._parse(attrs="show_id", value_type="int") if self._tv_id is None else self._tv_id + self.tvdb_id = self._parse(attrs=["external_ids", "tvdb_id"], value_type="int") + self.tvrage_id = self._parse(attrs=["external_ids", "tvrage_id"], value_type="int") + self.videos = self._parse(attrs=["videos", "results"], value_type="video", is_list=True) + self.vote_average = self._parse(attrs="vote_average", value_type="float") + self.vote_count = self._parse(attrs="vote_count", value_type="int") + self._finish(self.name) + + def _full_load(self): + return self._api.tv_episodes_get_details( + self.tv_id, self.season_number, self.episode_number, + language=self._tmdb.language, + include_image_language=self._tmdb._include_language, + include_video_language=self._tmdb._include_language, + append_to_response="account_states,credits,external_ids,images,translations,videos" + ) + + def _rate(self, rating): + self._api.tv_episodes_rate_tv_episode(self.tv_id, self.season_number, self.episode_number, rating) + + def _delete_rate(self): + self._api.tv_episodes_delete_rating(self.tv_id, self.season_number, self.episode_number) + + +class EpisodeGroup(TMDbReload): + """ Represents a single Episode Group. + + Attributes: + description (str): Episode Group Description. + episode_count (int): Episode Group Episode Count. + group_count (int): Number of Groups in the Episode Group. + groups (List[:class:`~tmdbapis.objs.simple.Group`]): Groups in the Episode Group. + id (str): Episode Group ID. + name (str): Episode Group Name. + network (:class:`~tmdbapis.objs.reload.Network`): Episode Group Network. + type (int): Episode Group Type. + """ + + def _load(self, data): + super()._load(data) + self.description = self._parse(attrs="description") + self.episode_count = self._parse(attrs="episode_count", value_type="int") + self.group_count = self._parse(attrs="group_count", value_type="int") + self.groups = self._parse(attrs="groups", value_type="group", is_list=True) + self.id = self._parse(attrs="id") + self.name = self._parse(attrs="name") + self.network = self._parse(attrs="network", value_type="network") + self.type = self._parse(attrs="type", value_type="int") + self._finish(self.name) + + def _full_load(self): + return self._api.tv_episode_groups_get_details(self.id, language=self._tmdb.language) + + +class Keyword(TMDbReload): + """ Represents a single Keyword. + + Attributes: + id (int): Keyword ID. + name (str): Keyword Name. + movies (List[:class:`~tmdbapis.objs.reload.Movie`]): Keyword Movies. + tv_shows (List[:class:`~tmdbapis.objs.reload.TVShow`]): Keyword TV Shows. + """ + + def _load(self, data): + super()._load(data) + self.id = self._parse(attrs="id", value_type="int") + self.name = self._parse(attrs="name") + self._finish(self.name) + + def _full_load(self): + return self._api.keywords_get_details(self.id, language=self._tmdb.language) + + @property + def movies(self): + return self._api.discover_movie_discover(with_keywords=self.id) + + @property + def tv_shows(self): + return self._api.discover_tv_discover(with_keywords=self.id) + + +class Movie(TMDbReload, Favorite, Rate, Watchlist): + """ Represents a single Episode. + + Attributes: + adult (bool): Is the Movie an adult movie. + alternative_titles (List[:class:`~tmdbapis.objs.simple.AlternativeTitle`]): Movie Alternative Titles. + backdrop_path (str): Backdrop Path. + backdrop_url (str): Backdrop Full URL. + backdrops (List[:class:`~tmdbapis.objs.image.Still`]): List of other Backdrops for the Movie. + budget (int): Movie Budget. + cast (List[:class:`~tmdbapis.objs.reload.Credit`]): List of Movie Cast Credits. + collection (:class:`~tmdbapis.objs.reload.Collection`): Movie's Collection. + companies (List[:class:`~tmdbapis.objs.reload.Company`]): List of Production Companies for the Movie. + countries (List[:class:`~tmdbapis.objs.simple.Country`]): List of Production Countries for the Movie. + crew (List[:class:`~tmdbapis.objs.reload.Credit`]): List of Movie Crew Credits. + facebook_id (str): Facebook ID for the Movie. + favorite (bool): If this Movie has been marked as a favorite. (Authentication Required) + genres (List[:class:`~tmdbapis.objs.simple.Genre`]): List of Genres for the Movie. + homepage (str): Homepage for the Movie. + id (str): Movie ID. + imdb_id (str): IMDb ID for the Movie. + instagram_id (str): Instagram ID for the Movie. + keywords (List[:class:`~tmdbapis.objs.reload.Keyword`]): List of Keywords for the Movie. + spoken_languages (List[:class:`~tmdbapis.objs.simple.Language`]): List of Spoken Languages for the Movie. + lists (:class:`~tmdbapis.objs.pagination.MovieLists`): Pagination Object of Lists containing the Movie. + logos (List[:class:`~tmdbapis.objs.image.Logo`]): List of other Logos for the Movie. + original_language (:class:`~tmdbapis.objs.simple.Language`): Original Language of the Movie. + original_title (str): Movie's Original Title. + overview (str): Movie Overview. + popularity (float): Movie's Popularity. + poster_path (str): Poster Path. + poster_url (str): Poster Full URL. + posters (List[:class:`~tmdbapis.objs.image.Poster`]): List of other Posters for the Movie. + rated (Union[float, bool]): Your rating for this Movie or false if you have not rated it. (Authentication Required) + recommendations (:class:`~tmdbapis.objs.pagination.RecommendedMovies`): Pagination Object of Recommended Movies based on this Movie. + release_date (datetime): Movie's Primary Release Date. + release_dates (Dict[str, :class:`~tmdbapis.objs.simple.ReleaseDate`]): Dictionary of Release Dates where the keys are the ISO 3166-1 Alpha-2 Country Codes. + revenue (int): Movie's Revenue. + reviews (class:`~tmdbapis.objs.pagination.MovieReviews`): Pagination Object of Movie Reviews for this Movie. + runtime (int) Movie's Runtime. + similar (class:`~tmdbapis.objs.pagination.SimilarMovies`): Pagination Object of Similar Movie to this Movie. + status (str): Movie's Status. + tagline (str): Movie's Tagline. + title (str): Movie's Title. + trailers (List[:class:`~tmdbapis.objs.simple.Trailer`]): List of Trailers for the Movie. + translations (List[:class:`~tmdbapis.objs.simple.Translation`]): List of Translations for the Movie. + twitter_id (str): Twitter ID for the Movie. + video (bool): Unsure when this is true. + videos (List[:class:`~tmdbapis.objs.simple.Video`]): List of Videos associated with the Movie. + vote_average (float): Vote Average for the Movie. + vote_count (int): Number of Votes for the Movie. + watch_providers (Dict[str, :class:`~tmdbapis.objs.simple.CountryWatchProviders`]): Dictionary of Watch Providers where the keys are the ISO 3166-1 Alpha-2 Country Codes. + watchlist (bool): If this Movie has been added to your watchlist. (Authentication Required) + """ + + def _load(self, data): + super()._load(data) + self.adult = self._parse(attrs="adult", value_type="bool") + self.alternative_titles = self._parse(attrs=["alternative_titles", "titles"], value_type="alternative_title", + is_list=True) + self.backdrop_path = self._parse(attrs="backdrop_path") + self.backdrop_url = self._image_url(self.backdrop_path) + self.backdrops = self._parse(attrs=["images", "backdrops"], value_type="backdrop", is_list=True) + self.budget = self._parse(attrs="budget", value_type="int") + self.cast = self._parse(attrs=["credits", "cast"], value_type="movie_cast", is_list=True) + self.collection = self._parse(attrs="belongs_to_collection", value_type="collection") + self.companies = self._parse(attrs="production_companies", value_type="company", is_list=True) + self.countries = self._parse(attrs="production_countries", value_type="country", is_list=True) + self.crew = self._parse(attrs=["credits", "crew"], value_type="movie_crew", is_list=True) + self.facebook_id = self._parse(attrs=["external_ids", "facebook_id"]) + self.favorite = self._parse(attrs=["account_states", "favorite"], value_type="bool") + self.genres = self._parse(attrs="genres" if "genres" in self._data else "genre_ids", value_type="movie_genre", + is_list=True) + self.homepage = self._parse(attrs="homepage") + self.id = self._parse(attrs="id", value_type="int") + self.imdb_id = self._parse(attrs="imdb_id") + self.instagram_id = self._parse(attrs=["external_ids", "instagram_id"]) + self.keywords = self._parse(attrs=["keywords", "keywords"], value_type="keyword", is_list=True) + self.spoken_languages = self._parse(attrs="spoken_languages", value_type="language", is_list=True) + self.lists = self._parse(attrs="lists", value_type="lists", key=self.id) + self.logos = self._parse(attrs=["images", "logos"], value_type="logo", is_list=True) + self.original_language = self._parse(attrs="original_language", value_type="language") + self.original_title = self._parse(attrs="original_title") + self.overview = self._parse(attrs="overview") + self.popularity = self._parse(attrs="popularity", value_type="float") + self.poster_path = self._parse(attrs="poster_path") + self.poster_url = self._image_url(self.poster_path) + self.posters = self._parse(attrs=["images", "posters"], value_type="poster", is_list=True) + try: + self.rated = self._parse(attrs=["account_states", "rated"], value_type="float") + except ValueError: + self.rated = self._parse(attrs=["account_states", "rated"], value_type="bool") + self.recommendations = self._parse(attrs="recommendations", value_type="recommended_movies", key=self.id) + self.release_date = self._parse(attrs="release_date", value_type="date") + self.release_dates = {} + if "release_dates" in self._data and "results" in self._data["release_dates"]: + for iso in self._data["release_dates"]["results"]: + self.release_dates[iso["iso_3166_1"]] = self._parse(data=iso, attrs="release_dates", + value_type="release_date", is_list=True) + self.revenue = self._parse(attrs="revenue", value_type="int") + self.reviews = self._parse(attrs="reviews", value_type="movie_reviews", key=self.id) + self.runtime = self._parse(attrs="runtime", value_type="int") + self.similar = self._parse(attrs="similar", value_type="similar_movies", key=self.id) + self.status = self._parse(attrs="status") + self.tagline = self._parse(attrs="tagline") + self.title = self._parse(attrs="title") + self.trailers = self._parse(attrs=["trailers", "youtube"], value_type="trailer", is_list=True) + self.translations = self._parse(attrs=["translations", "translations"], value_type="translation", is_list=True) + self.twitter_id = self._parse(attrs=["external_ids", "twitter_id"]) + self.video = self._parse(attrs="video", value_type="bool") + self.videos = self._parse(attrs=["videos", "results"], value_type="video", is_list=True) + self.vote_average = self._parse(attrs="vote_average", value_type="float") + self.vote_count = self._parse(attrs="vote_count", value_type="int") + self.watch_providers = self._parse(attrs=["watch/providers", "results"], value_type="country_watch_provider", + is_dict=True) + self.watchlist = self._parse(attrs=["account_states", "watchlist"], value_type="bool") + self._finish(self.title) + + def _full_load(self): + return self._api.movies_get_details( + self.id, + language=self._tmdb.language, + include_image_language=self._tmdb._include_language, + include_video_language=self._tmdb._include_language, + append_to_response="account_states,alternative_titles,credits,external_ids,images," + "keywords,lists,recommendations,release_dates,reviews,similar," + "trailers,translations,videos,watch/providers" + ) + + def _rate(self, rating): + self._api.movies_rate_movie(self.id, rating) + + def _delete_rate(self): + self._api.movies_delete_rating(self.id) + + def _media_type(self): + return "movie" + + +class Network(TMDbReload): + """ Represents a single Network. + + Attributes: + alternative_names (List[:class:`~tmdbapis.objs.simple.AlternativeName`]): Network Alternative Names. + country (:class:`~tmdbapis.objs.simple.Country`): Network Country. + headquarters (str): Network Headquarters. + homepage (str): Network Homepage. + id (int): Network ID. + logo_path (str): Logo Path. + logo_url (str): Logo Full URL. + logos (List[:class:`~tmdbapis.objs.image.Logo`]): List of other Logos for the Network. + name (str): Network Name. + tv_shows (List[:class:`~tmdbapis.objs.reload.TVShow`]): Network TV Shows. + """ + + def _load(self, data): + super()._load(data) + self.alternative_names = self._parse(attrs=["alternative_names", "results"], + value_type="alternative_name", is_list=True) + self.country = self._parse(attrs="origin_country", value_type="country") + self.headquarters = self._parse(attrs="headquarters") + self.homepage = self._parse(attrs="homepage") + self.id = self._parse(attrs="id", value_type="int") + self.logo_path = self._parse(attrs="logo_path") + self.logo_url = self._image_url(self.logo_path) + self.logos = self._parse(attrs=["images", "logos"], value_type="logo", is_list=True) + self.name = self._parse(attrs="name") + self._finish(self.name) + + def _full_load(self): + return self._api.networks_get_details(self.id, append_to_response="alternative_names,images") + + @property + def tv_shows(self): + return self._api.discover_tv_discover(with_networks=self.id) + + +class Person(TMDbReload): + """ Represents a single Person. + + Attributes: + adult (bool): Is the Person an adult actor. + also_known_as (List[str]): Name's this Person is also known as. + biography (str): Person's Biography. + birthday (datetime): Person's Birthday. + deathday (datetime): Person's Deathday. + facebook_id (str): Facebook ID for the Person. + freebase_id (str): Freebase ID for the Person. + freebase_mid (str): Freebase MID for the Person. + gender (int): Person's Gender. (1: Women, 2: Men) + homepage (str): Homepage for the Person. + id (str): Person ID. + imdb_id (str): IMDb ID for the Person. + instagram_id (str): Instagram ID for the Person. + known_for_department (str): Department the Person is best known for. + name (str): Person's Name. + place_of_birth (str): Person's Place of Birth. + popularity (float): Person's Popularity. + profile_path (str): Profile Path. + profile_url (str): Profile Full URL. + profiles (List[:class:`~tmdbapis.objs.image.Profile`]): List of other Profiles for the Person. + movie_cast (List[:class:`~tmdbapis.objs.reload.Credit`]): List of Movie Cast Credits for the Person. + movie_crew (List[:class:`~tmdbapis.objs.reload.Credit`]): List of Movie Crew Credits for the Person. + tagged (class:`~tmdbapis.objs.pagination.TaggedImages`): Pagination Object of Tagged Images of this Person. + translations (List[:class:`~tmdbapis.objs.simple.Translation`]): List of Translations for the Person. + tv_cast (List[:class:`~tmdbapis.objs.reload.Credit`]): List of TV Cast Credits for the Person. + tv_crew (List[:class:`~tmdbapis.objs.reload.Credit`]): List of TV Crew Credits for the Person. + tvrage_id (str): TVRage ID for the Person. + twitter_id (str): Twitter ID for the Person. + """ + + def _load(self, data): + super()._load(data) + self.adult = self._parse(attrs="adult", value_type="bool") + self.also_known_as = self._parse(attrs="also_known_as", is_list=True) + self.biography = self._parse(attrs="biography") + self.birthday = self._parse(attrs="birthday", value_type="date") + self.deathday = self._parse(attrs="deathday", value_type="date") + self.facebook_id = self._parse(attrs=["external_ids", "facebook_id"]) + self.freebase_id = self._parse(attrs=["external_ids", "freebase_id"]) + self.freebase_mid = self._parse(attrs=["external_ids", "freebase_mid"]) + self.gender = self._parse(attrs="gender", value_type="int") + self.homepage = self._parse(attrs="homepage") + self.id = self._parse(attrs="id", value_type="int") + self.imdb_id = self._parse(attrs="imdb_id") + self.instagram_id = self._parse(attrs=["external_ids", "instagram_id"]) + self.known_for_department = self._parse(attrs="known_for_department") + self.name = self._parse(attrs="name") + self.place_of_birth = self._parse(attrs="place_of_birth") + self.popularity = self._parse(attrs="popularity", value_type="float") + self.profile_path = self._parse(attrs="profile_path") + self.profile_url = self._image_url(self.profile_path) + self.profiles = self._parse(attrs=["images", "profiles"], value_type="profile", is_list=True) + self.movie_cast = self._parse(attrs=["movie_credits", "cast"], value_type="movie_cast", is_list=True) + self.movie_crew = self._parse(attrs=["movie_credits", "crew"], value_type="movie_crew", is_list=True) + self.tagged = self._parse(attrs="tagged_images", value_type="tagged_images", key=self.id) + self.translations = self._parse(attrs=["translations", "translations"], value_type="translation", is_list=True) + self.tv_cast = self._parse(attrs=["tv_credits", "cast"], value_type="tv_cast", is_list=True) + self.tv_crew = self._parse(attrs=["tv_credits", "crew"], value_type="tv_crew", is_list=True) + self.tvrage_id = self._parse(attrs=["external_ids", "tvrage_id"], value_type="int") + self.twitter_id = self._parse(attrs=["external_ids", "twitter_id"]) + self._finish(self.name) + + def _full_load(self): + return self._api.people_get_details( + self.id, + language=self._tmdb.language, + append_to_response="movie_credits,tv_credits,external_ids,images,tagged_images,translations" + ) + + +class Review(TMDbReload): + """ Represents a single Review. + + Attributes: + author (str): Review Author. + avatar_path (str): Review Author Avatar Path. + avatar_url (str): Review Author Avatar Full URL. + content (str): Review content. + created_at (datetime): Date Review was Created. + id (str): Review ID. + iso_639_1 (str): Default ISO 639-1 Language Code of the Review. + language (:class:`~tmdbapis.objs.simple.Language`): Language object for the ISO 639-1 Language Code. + media_id (int): Media ID of the Review. + media_title (str): Media Title of the Review. + media_type (str): Media Type of the Media ID. + rating (float): Review Rating. + updated_at (datetime): Date Review was Updated. + url (str): Review URL. + username (str): Review Author Username. + """ + + def _load(self, data): + super()._load(data) + self.author = self._parse(attrs="author") + self.avatar_path = self._parse(attrs=["author_details", "avatar_path"]) + self.avatar_url = self._image_url(self.avatar_path) + self.content = self._parse(attrs="content") + self.created_at = self._parse(attrs="created_at", value_type="date") + self.id = self._parse(attrs="review_id" if "review_id" in self._data else "id") + self.iso_639_1 = self._parse(attrs="iso_639_1") + self.language = self._tmdb._get_object(self._data, "language") + self.media_id = self._parse(attrs="media_id", value_type="int") + self.media_title = self._parse(attrs="media_title") + self.media_type = self._parse(attrs="media_type") + self.rating = self._parse(attrs=["author_details", "rating"], value_type="float") + self.updated_at = self._parse(attrs="updated_at", value_type="date") + self.url = self._parse(attrs="url") + self.username = self._parse(attrs=["author_details", "username"]) + self._finish(self.author) + + def _full_load(self): + return self._api.reviews_get_details(self.id) + + +class Season(TMDbReload): + """ Represents a single Season. + + Attributes: + aggregate_cast (List[:class:`~tmdbapis.objs.reload.Credit`]): List of Season Aggregate Crew Credits. + aggregate_crew (List[:class:`~tmdbapis.objs.reload.Credit`]): List of Season Aggregate Crew Credits. + air_date (datetime): Season Air Date. + cast (List[:class:`~tmdbapis.objs.reload.Credit`]): List of Season Cast Credits. + crew (List[:class:`~tmdbapis.objs.reload.Credit`]): List of Season Crew Credits. + episodes (List[:class:`~tmdbapis.objs.reload.Episode`]): List of Episodes in the Season. + freebase_id (str): Freebase ID for the Season. + freebase_mid (str): Freebase MID for the Season. + id (int): Season ID. + name (str): Season Name. + poster_path (str): Poster Path. + poster_url (str): Poster Full URL. + posters (List[:class:`~tmdbapis.objs.image.Poster`]): List of other Posters for the Season. + overview (str): Season Overview. + season_number (int): Season Number. + translations (List[:class:`~tmdbapis.objs.simple.Translation`]): List of Translations for the Season. + tv_id (int): TMDb TV Show ID the contains the Season. + tvdb_id (int): TVDB ID of the Season. + tvrage_id (int): TV Rage ID of the Season. + videos (List[:class:`~tmdbapis.objs.simple.Video`]): List of Videos associated with the Season. + """ + + def __init__(self, tmdb, data, tv_id, load=False): + self._tv_id = tv_id + super().__init__(tmdb, data=data, load=load) + + def _load(self, data): + super()._load(data) + self.aggregate_cast = self._parse(attrs=["aggregate_credits", "cast"], value_type="agg_tv_cast", extend=True) + self.aggregate_crew = self._parse(attrs=["aggregate_credits", "crew"], value_type="agg_tv_crew", extend=True) + self.air_date = self._parse(attrs="air_date", value_type="date") + self.cast = self._parse(attrs=["credits", "cast"], value_type="tv_cast", is_list=True) + self.crew = self._parse(attrs=["credits", "crew"], value_type="tv_crew", is_list=True) + self.tv_id = self._tv_id + self.episodes = self._parse(attrs="episodes", value_type="episode", is_list=True, key=self.tv_id) + self.freebase_id = self._parse(attrs=["external_ids", "freebase_id"]) + self.freebase_mid = self._parse(attrs=["external_ids", "freebase_mid"]) + self.id = self._parse(attrs="id", value_type="int") + self.name = self._parse(attrs="name") + self.overview = self._parse(attrs="overview") + self.poster_path = self._parse(attrs="poster_path") + self.poster_url = self._image_url(self.poster_path) + self.posters = self._parse(attrs=["images", "posters"], value_type="poster", is_list=True) + self.season_number = self._parse(attrs="season_number", value_type="int") + self.translations = self._parse(attrs=["translations", "translations"], value_type="translation", is_list=True) + self.tvdb_id = self._parse(attrs=["external_ids", "tvdb_id"], value_type="int") + self.tvrage_id = self._parse(attrs=["external_ids", "tvrage_id"], value_type="int") + self.videos = self._parse(attrs=["videos", "results"], value_type="video", is_list=True) + self._finish(self.name) + + def _full_load(self): + return self._api.tv_seasons_get_details( + self.tv_id, self.season_number, + language=self._tmdb.language, + include_image_language=self._tmdb._include_language, + include_video_language=self._tmdb._include_language, + append_to_response="account_states,aggregate_credits,credits,external_ids,images,translations,videos" + ) + + +class TVShow(TMDbReload, Favorite, Rate, Watchlist): + """ Represents a single TV Show. + + Attributes: + aggregate_cast (List[:class:`~tmdbapis.objs.reload.Credit`]): List of Aggregate Crew Credits. + aggregate_crew (List[:class:`~tmdbapis.objs.reload.Credit`]): List of Aggregate Crew Credits. + alternative_titles (List[:class:`~tmdbapis.objs.simple.AlternativeTitle`]): TV Show Alternative Titles. + backdrop_path (str): Backdrop Path. + backdrop_url (str): Backdrop Full URL. + backdrops (List[:class:`~tmdbapis.objs.image.Still`]): List of other Backdrops for the TV Show. + cast (List[:class:`~tmdbapis.objs.reload.Credit`]): List of TV Show Cast Credits. + companies (List[:class:`~tmdbapis.objs.reload.Company`]): List of Production Companies for the TV Show. + content_ratings (Dict[str, str]): Dictionary of Content Ratings where the keys are the ISO 3166-1 Alpha-2 Country Codes. + countries (List[:class:`~tmdbapis.objs.simple.Country`]): List of Production Countries for the TV Show. + created_by (List[:class:`~tmdbapis.objs.reload.Credit`]): List of Credits of who Created the TV Show. + crew (List[:class:`~tmdbapis.objs.reload.Credit`]): List of TV Show Crew Credits. + episode_groups (List[:class:`~tmdbapis.objs.reload.EpisodeGroup`]): List of TV Show Episode Groups. + episode_run_time (List[int]): List of Episode Run Times in this TV Show. + facebook_id (str): Facebook ID for the TV Show. + favorite (bool): If this TV Show has been marked as a favorite. (Authentication Required) + first_air_date (datetime): Date TV Show was First Aired. + freebase_id (str): Freebase ID for the TV Show. + freebase_mid (str): Freebase MID for the TV Show. + genres (List[:class:`~tmdbapis.objs.simple.Genre`]): List of Genres for the TV Show. + homepage (str): Homepage for the TV Show. + id (str): TV Show ID. + imdb_id (str): IMDb ID for the TV Show. + in_production (bool): If the TV Show is in Production or not. + instagram_id (str): Instagram ID for the TV Show. + keywords (List[:class:`~tmdbapis.objs.reload.Keyword`]): List of Keywords for the TV Show. + languages (List[:class:`~tmdbapis.objs.simple.Language`]): List of Languages for the TV Show. + last_air_date (datetime): Date TV Show was Last Aired. + last_episode_to_air (:class:`~tmdbapis.objs.reload.Episode`): Last Episode to Air. + logos (List[:class:`~tmdbapis.objs.image.Logo`]): List of other Logos for the TV Show. + name (str): TV Show's Name. + networks (List[:class:`~tmdbapis.objs.reload.Network`]): List of Networks for the TV Show. + next_episode_to_air (:class:`~tmdbapis.objs.reload.Episode`): Next Episode to Air. + number_of_episodes (int): Number of Episodes in the TV Show. + number_of_seasons (int): Number of Seasons in the TV Show. + origin_countries (:class:`~tmdbapis.objs.simple.Country`): Origin Countries of the TV Show. + original_language (:class:`~tmdbapis.objs.simple.Language`): Original Language of the TV Show. + original_name (str): TV Show's Original Name. + overview (str): TV Show Overview. + popularity (float): TV Show's Popularity. + poster_path (str): Poster Path. + poster_url (str): Poster Full URL. + posters (List[:class:`~tmdbapis.objs.image.Poster`]): List of other Posters for the TV Show. + rated (Union[float, bool]): Your rating for this TV Show or false if you have not rated it. (Authentication Required) + recommendations (:class:`~tmdbapis.objs.pagination.RecommendedTVShows`): Pagination Object of Recommended TV Show based on this TV Show. + seasons (List[:class:`~tmdbapis.objs.reload.Season`]): List of Seasons in the TV Show. + similar (class:`~tmdbapis.objs.pagination.SimilarTVShows`): Pagination Object of Similar TV Show to this TV Show. + spoken_languages (List[:class:`~tmdbapis.objs.simple.Language`]): List of Spoken Languages for the TV Show. + status (str): TV Show's Status. + tagline (str): TV Show's Tagline. + translations (List[:class:`~tmdbapis.objs.simple.Translation`]): List of Translations for the TV Show. + tvdb_id (int): TVDB ID of the TV Show. + tvrage_id (int): TV Rage ID of the TV Show. + twitter_id (str): Twitter ID for the TV Show. + type (str): Type of TV Show. + videos (List[:class:`~tmdbapis.objs.simple.Video`]): List of Videos associated with the TV Show. + vote_average (float): Vote Average for the TV Show. + vote_count (int): Number of Votes for the TV Show. + watch_providers (Dict[str, :class:`~tmdbapis.objs.simple.CountryWatchProviders`]): Dictionary of Watch Providers where the keys are the ISO 3166-1 Alpha-2 Country Codes. + watchlist (bool): If this TV Show has been added to your watchlist. (Authentication Required) + """ + + def _load(self, data): + super()._load(data) + self.aggregate_cast = self._parse(attrs=["aggregate_credits", "cast"], value_type="agg_tv_cast", extend=True) + self.aggregate_crew = self._parse(attrs=["aggregate_credits", "crew"], value_type="agg_tv_crew", extend=True) + self.alternative_titles = self._parse(attrs=["alternative_titles", "results"], + value_type="alternative_title", is_list=True) + self.backdrop_path = self._parse(attrs="backdrop_path") + self.backdrop_url = self._image_url(self.backdrop_path) + self.backdrops = self._parse(attrs=["images", "backdrops"], value_type="backdrop", is_list=True) + self.cast = self._parse(attrs=["credits", "cast"], value_type="tv_cast", is_list=True) + self.companies = self._parse(attrs="production_companies", value_type="company", is_list=True) + self.content_ratings = self._parse(attrs=["content_ratings", "results"], value_type="content_rating") + self.countries = self._parse(attrs="production_countries", value_type="country", is_list=True) + self.created_by = self._parse(attrs="created_by", value_type="tv_cast", is_list=True) + self.crew = self._parse(attrs=["credits", "crew"], value_type="tv_crew", is_list=True) + self.episode_groups = self._parse(attrs=["episode_groups", "results"], value_type="episode_group", is_list=True) + self.episode_run_time = self._parse(attrs="episode_run_time", value_type="int", is_list=True) + self.facebook_id = self._parse(attrs=["external_ids", "facebook_id"]) + self.favorite = self._parse(attrs=["account_states", "favorite"], value_type="bool") + self.first_air_date = self._parse(attrs="first_air_date", value_type="date") + self.freebase_id = self._parse(attrs=["external_ids", "freebase_id"]) + self.freebase_mid = self._parse(attrs=["external_ids", "freebase_mid"]) + self.genres = self._parse(attrs="genres" if "genres" in self._data else "genre_ids", value_type="tv_genre", is_list=True) + self.homepage = self._parse(attrs="homepage") + self.id = self._parse(attrs="id", value_type="int") + self.imdb_id = self._parse(attrs=["external_ids", "imdb_id"]) + self.in_production = self._parse(attrs="in_production", value_type="bool") + self.instagram_id = self._parse(attrs=["external_ids", "instagram_id"]) + self.keywords = self._parse(attrs=["keywords", "results"], value_type="keyword", is_list=True) + self.languages = self._parse(attrs="languages", value_type="language", is_list=True) + self.last_air_date = self._parse(attrs="last_air_date", value_type="date") + self.last_episode_to_air = self._parse(attrs="last_episode_to_air", value_type="episode", key=self.id) + self.logos = self._parse(attrs=["images", "logos"], value_type="logo", is_list=True) + self.name = self._parse(attrs="name") + self.networks = self._parse(attrs="networks", value_type="network", is_list=True) + self.next_episode_to_air = self._parse(attrs="next_episode_to_air", value_type="episode", key=self.id) + self.number_of_episodes = self._parse(attrs="number_of_episodes", value_type="int") + self.number_of_seasons = self._parse(attrs="number_of_seasons", value_type="int") + self.origin_countries = self._parse(attrs="origin_country", value_type="country", is_list=True) + self.original_language = self._parse(attrs="original_language", value_type="language") + self.original_name = self._parse(attrs="original_name") + self.overview = self._parse(attrs="overview") + self.popularity = self._parse(attrs="popularity", value_type="float") + self.poster_path = self._parse(attrs="poster_path") + self.poster_url = self._image_url(self.poster_path) + self.posters = self._parse(attrs=["images", "posters"], value_type="poster", is_list=True) + try: + self.rated = self._parse(attrs=["account_states", "rated"], value_type="float") + except ValueError: + self.rated = self._parse(attrs=["account_states", "rated"], value_type="bool") + self.recommendations = self._parse(attrs="recommendations", value_type="recommended_tv", key=self.id) + self.seasons = self._parse(attrs="seasons", value_type="season", is_list=True, key=self.id) + self.similar = self._parse(attrs="similar", value_type="similar_tv", key=self.id) + self.spoken_languages = self._parse(attrs="spoken_languages", value_type="language", is_list=True) + self.status = self._parse(attrs="status") + self.tagline = self._parse(attrs="tagline") + self.translations = self._parse(attrs=["translations", "translations"], value_type="translation", is_list=True) + self.tvdb_id = self._parse(attrs=["external_ids", "tvdb_id"], value_type="int") + self.tvrage_id = self._parse(attrs=["external_ids", "tvrage_id"], value_type="int") + self.twitter_id = self._parse(attrs=["external_ids", "twitter_id"]) + self.type = self._parse(attrs="type") + self.videos = self._parse(attrs=["videos", "results"], value_type="video", is_list=True) + self.vote_average = self._parse(attrs="vote_average", value_type="float") + self.vote_count = self._parse(attrs="vote_count", value_type="int") + self.watch_providers = self._parse(attrs=["watch/providers", "results"], + value_type="country_watch_provider", is_dict=True) + self.watchlist = self._parse(attrs=["account_states", "watchlist"], value_type="bool") + self._finish(self.name) + + def _full_load(self): + return self._api.tv_get_details( + self.id, + language=self._tmdb.language, + include_image_language=self._tmdb._include_language, + include_video_language=self._tmdb._include_language, + append_to_response="account_states,aggregate_credits,alternative_titles,content_ratings," + "credits,episode_groups,external_ids,images,keywords,recommendations," + "similar,translations,videos,watch/providers" + ) + + def _rate(self, rating): + self._api.tv_rate_tv_show(self.id, rating) + + def _delete_rate(self): + self._api.tv_delete_rating(self.id) + + def _media_type(self): + return "tv" diff --git a/tmdbapis/objs/simple.py b/tmdbapis/objs/simple.py new file mode 100644 index 0000000..75aadb3 --- /dev/null +++ b/tmdbapis/objs/simple.py @@ -0,0 +1,397 @@ +from tmdbapis.exceptions import NotFound +from tmdbapis.objs.base import TMDbObj + + +class AlternativeName(TMDbObj): + """ Represents a single Alternative Name. + + Attributes: + name (str): Alternative Name. + type (str): Type of Alternative Name. + """ + + def _load(self, data): + super()._load(data) + self.name = self._parse(attrs="name") + self.type = self._parse(attrs="type") + self._finish(self.name) + + +class AlternativeTitle(TMDbObj): + """ Represents a single Alternative Title. + + Attributes: + name (str): Alternative Title. + type (str): Type of Alternative Title. + """ + + def _load(self, data): + super()._load(data) + self.name = self._parse(attrs="name") + self.type = self._parse(attrs="type") + self._finish(self.name) + + +class Certification(TMDbObj): + """ Represents a single Certification. + + Attributes: + certification (str): Certification text. + meaning (str): Certification meaning. + order (int): Certification Order. + """ + + def _load(self, data): + super()._load(data) + self.certification = self._parse(attrs="certification") + self.meaning = self._parse(attrs="meaning") + self.order = self._parse(attrs="order", value_type="int") + self._finish(self.certification) + + +class Country(TMDbObj): + """ Represents a single Country. + + Attributes: + iso_3166_1 (str): ISO 3166-1 Alpha-2 Code of the Country. + name (str): Country name. + native_name (str): Country native name. + """ + + def _load(self, data): + super()._load(data) + self.iso_3166_1 = self._parse(attrs="iso_3166_1") + self.name = self._parse(attrs="name" if "name" in self._data else "english_name") + self.native_name = self._parse(attrs="native_name") + self._finish(self.name) + + +class CountryCertifications(TMDbObj): + """ Represents a Country's Certifications. + + Attributes: + certifications (List[:class:`~tmdbapis.objs.simple.Certification`]): List of Certifications. + country (str): Certification country. + """ + + def __init__(self, tmdb, data, country): + self._country = country + super().__init__(tmdb, data) + + def _load(self, data): + super()._load(data) + self.certifications = self._parse(value_type="certification", is_list=True) + self.certifications.sort(key=lambda x: x.order) + self.country = self._country + self._finish(self.country) + + def __str__(self): + return f"{self._name} Certifications" + + +class CountryWatchProviders(TMDbObj): + """ Represents the Watch Providers for an item in the Country. + + Attributes: + buy (List[:class:`~tmdbapis.objs.simple.WatchProvider`]): Watch Provider's that can sell this item in the Country. + country (:class:`~tmdbapis.objs.simple.Country`): Country object for the ISO 3166-1 Country Code. + flatrate (List[:class:`~tmdbapis.objs.simple.WatchProvider`]): Watch Provider's that have this as part of their service's flatrate in the Country. + iso_3166_1 (str): ISO 3166-1 Alpha-2 Country Code of the Video. + link (int): Link to the Countries Watch Provider page for the item. + rent (List[:class:`~tmdbapis.objs.simple.WatchProvider`]): Watch Provider's that can rent this item in the Country. + """ + + def __init__(self, tmdb, data, country): + self._country = country + super().__init__(tmdb, data) + + def _load(self, data): + super()._load(data) + self.buy = self._parse(attrs="buy", value_type="watch_provider", is_list=True) + self.country = self._tmdb._get_object(self._data, "country") + self.flatrate = self._parse(attrs="flatrate", value_type="watch_provider", is_list=True) + self.iso_3166_1 = self._country + self.link = self._parse(attrs="link") + self.rent = self._parse(attrs="rent", value_type="watch_provider", is_list=True) + self._finish(self.country) + + def __str__(self): + return f"{self._name} Watch Providers" + + +class Department(TMDbObj): + """ Represents a single Department. + + Attributes: + department (int): Department name. + jobs (List[str]): List of jobs within the department. + """ + + def _load(self, data): + super()._load(data) + self.department = self._parse(attrs="department") + self.jobs = self._parse(attrs="jobs", is_list=True) + self._finish(self.department) + + +class FindResults(TMDbObj): + """ Represents a Find search result. + + Attributes: + movie_results (List[:class:`~tmdbapis.objs.reload.Movie`]): Movie results found. + person_results (List[:class:`~tmdbapis.objs.reload.Person`]): Person results found. + tv_episode_results (List[:class:`~tmdbapis.objs.reload.Episode`]): Episode results found. + tv_results (List[:class:`~tmdbapis.objs.reload.TVShow`]): TV Show results found. + tv_season_results (List[:class:`~tmdbapis.objs.reload.Season`]): Season results found. + """ + + def __init__(self, tmdb, external_id, external_source): + self._external_id = external_id + self._external_source = external_source + super().__init__(tmdb, None) + + def _load(self, data): + super()._load(self._api.find_find_by_id(self._external_id, self._external_source, language=self._tmdb.language)) + self.movie_results = self._parse(attrs="movie_results", value_type="movie", is_list=True) + self.person_results = self._parse(attrs="person_results", value_type="person", is_list=True) + self.tv_episode_results = self._parse(attrs="tv_episode_results", value_type="episode", is_list=True) + self.tv_results = self._parse(attrs="tv_results", value_type="tv", is_list=True) + self.tv_season_results = self._parse(attrs="tv_season_results", value_type="season", is_list=True) + if not self.movie_results and not self.person_results and not self.tv_results and not self.tv_episode_results and not self.tv_season_results: + raise NotFound(f"No Results were found for {self._external_source}: {self._external_id}") + self._finish("FindResults Results") + + +class Genre(TMDbObj): + """ Represents a single Genre. + + Attributes: + id (int): Genre ID + name (str): Genre name. + """ + + def _load(self, data): + super()._load(data) + self.id = self._parse(attrs="id", value_type="int") + self.name = self._parse(attrs="name") + self._finish(self.name) + + +class Group(TMDbObj): + """ Represents a single Group of Episodes. + + Attributes: + episode_group_id (int): Episode Group ID. + episodes (List[:class:`~tmdbapis.objs.reload.Episode`]): List of episodes in the group. + locked (bool): Is Locked. + name (str): Group name. + order (str): Group order in the Episode Group. + """ + + def _load(self, data): + super()._load(data) + self.episode_group_id = self._parse(attrs="id") + self.episodes = self._parse(attrs="episodes", value_type="episode", is_list=True) + self.locked = self._parse(attrs="locked", value_type="bool") + self.name = self._parse(attrs="name") + self.order = self._parse(attrs="order", value_type="int") + self._finish(self.name) + + +class Language(TMDbObj): + """ Represents a single Language. + + Attributes: + english_name (str): Language's english name. + iso_639_1 (str): ISO 639-1 Code of the Language. + name (str): Language name. + """ + + def _load(self, data): + super()._load(data) + self.english_name = self._parse(attrs="english_name") + self.iso_639_1 = self._parse(attrs="iso_639_1") + self.name = self._parse(attrs="name") + self._finish(self.english_name) + + +class ReleaseDate(TMDbObj): + """ Represents a Movie's Release Date. + + Release dates support different types: + + 1. Premiere + 2. Theatrical (limited) + 3. Theatrical + 4. Digital + 5. Physical + 6. TV + + Attributes: + certification (str): Movie's Certification. + iso_639_1 (str): ISO 639-1 Language Code of the Release Date. + language (:class:`~tmdbapis.objs.simple.Language`): Language object for the ISO 639-1 Language Code. + note (str): Note about the release date. + release_date (datetime): Movie's release date. + type (int): Type of release. + """ + + def _load(self, data): + super()._load(data) + self.certification = self._parse(attrs="certification") + self.iso_639_1 = self._parse(attrs="iso_639_1") + self.language = self._tmdb._get_object(self._data, "language") + self.note = self._parse(attrs="note") + self.release_date = self._parse(attrs="release_date", value_type="date") + self.type = self._parse(attrs="type", value_type="int") + self._finish(self.release_date) + + +class Timezones(TMDbObj): + """ Represents the Timezones of a Country. + + Attributes: + iso_3166_1 (str): ISO 3166-1 Alpha-2 Country Code of the Timezones. + zones (List[str]): List of timezones. + """ + + def _load(self, data): + super()._load(data) + self.iso_3166_1 = self._parse(attrs="iso_3166_1") + self.zones = self._parse(attrs="zones", is_list=True) + self._finish(self.iso_3166_1) + + +class Trailer(TMDbObj): + """ Represents a single Trailer. + + Attributes: + name (str): Trailer name. + size (str): Trailer size. + source (str): Trailer source. + type (str): Trailer type. + """ + + def _load(self, data): + super()._load(data) + self.name = self._parse(attrs="name") + self.size = self._parse(attrs="size") + self.source = self._parse(attrs="source") + self.type = self._parse(attrs="type") + self._finish(self.name) + + +class Translation(TMDbObj): + """ Represents a single Translation. + + Attributes: + biography (str): Translated Biography of :class:`~tmdbapis.objs.reload.Person` + country (:class:`~tmdbapis.objs.simple.Country`): Country object for the ISO 3166-1 Country Code. + english_language_name (str): Language English Name. + homepage (str): Translated Homepage of :class:`~tmdbapis.objs.reload.Collection`, :class:`~tmdbapis.objs.reload.Movie`, and :class:`~tmdbapis.objs.reload.TVShow` + iso_3166_1 (str): ISO 3166-1 Alpha-2 Country Code of the Translation. + iso_639_1 (str): ISO 639-1 Language Code of the Translation. + language (:class:`~tmdbapis.objs.simple.Language`): Language object for the ISO 639-1 Language Code. + language_name (str): Language Native Name. + name (str): Translated Name of :class:`~tmdbapis.objs.reload.TVShow`, :class:`~tmdbapis.objs.reload.Season`, and :class:`~tmdbapis.objs.reload.Episode`. + overview (str): Translated Overview of :class:`~tmdbapis.objs.reload.Collection`, :class:`~tmdbapis.objs.reload.Movie`, :class:`~tmdbapis.objs.reload.TVShow`, :class:`~tmdbapis.objs.reload.Season`, and :class:`~tmdbapis.objs.reload.Episode`. + title (str): Translated Title of :class:`~tmdbapis.objs.reload.Collection` and :class:`~tmdbapis.objs.reload.Movie`. + """ + + def _load(self, data): + super()._load(data) + self.country = self._tmdb._get_object(self._data, "country") + self.english_language_name = self._parse(attrs="english_name") + self.iso_3166_1 = self._parse(attrs="iso_3166_1") + self.iso_639_1 = self._parse(attrs="iso_639_1") + self.language = self._tmdb._get_object(self._data, "language") + self.language_name = self._parse(attrs="name") + if "data" in self._data and self._data["data"]: + if "biography" in self._data["data"]: + self.biography = self._parse(attrs=["data", "biography"]) + if "homepage" in self._data["data"]: + self.homepage = self._parse(attrs=["data", "homepage"]) + if "name" in self._data["data"]: + self.name = self._parse(attrs=["data", "name"]) + if "overview" in self._data["data"]: + self.overview = self._parse(attrs=["data", "overview"]) + if "title" in self._data["data"]: + self.title = self._parse(attrs=["data", "title"]) + + self._finish(self.english_language_name) + + +class User(TMDbObj): + """ Represents a single User. + + Attributes: + id (str): V4 User ID. + gravatar_hash (str): User's gravatar hash. + name (str) User's name. + username (str) User's username. + """ + + def _load(self, data): + super()._load(data) + self.id = self._parse(attrs="id") + self.gravatar_hash = self._parse(attrs="gravatar_hash") + self.name = self._parse(attrs="name") + self.username = self._parse(attrs="username") + self._finish(self.username) + + +class Video(TMDbObj): + """ Represents a single Video. + + Attributes: + country (:class:`~tmdbapis.objs.simple.Country`): Country object for the ISO 3166-1 Country Code. + id (str): Video ID. + iso_3166_1 (str): ISO 3166-1 Alpha-2 Country Code of the Video. + iso_639_1 (str): ISO 639-1 Language Code of the Video. + key (str): Video Key. + language (:class:`~tmdbapis.objs.simple.Language`): Language object for the ISO 639-1 Language Code. + name (str): Video Name. + official (bool): Official Video. + published_at (datetime): Date video was published at. + site (str): Video Site. + size (str): Video Size. + type (str): Video Type. + """ + + def _load(self, data): + super()._load(data) + self.country = self._tmdb._get_object(self._data, "country") + self.id = self._parse(attrs="id") + self.iso_3166_1 = self._parse(attrs="iso_3166_1") + self.iso_639_1 = self._parse(attrs="iso_639_1") + self.key = self._parse(attrs="key") + self.language = self._tmdb._get_object(self._data, "language") + self.name = self._parse(attrs="name") + self.official = self._parse(attrs="official", value_type="bool") + self.published_at = self._parse(attrs="published_at", value_type="date") + self.site = self._parse(attrs="site") + self.size = self._parse(attrs="size", value_type="int") + self.type = self._parse(attrs="type") + self._finish(self.name) + + +class WatchProvider(TMDbObj): + """ Represents a single Watch Provider. + + Attributes: + display_priority (int): Watch Provider display priority. + id (int): Watch Provider ID. + logo_path (str): Watch Provider logo path. + logo_url (str): Watch Provider full logo url. + name (str): WatchProvider Name. + """ + + def _load(self, data): + self._data = data + self._loading = True + self.display_priority = self._parse(attrs="display_priority", value_type="int") + self.id = self._parse(attrs="provider_id", value_type="int") + self.logo_path = self._parse(attrs="logo_path") + self.logo_url = self._image_url(self.logo_path) + self.name = self._parse(attrs="provider_name") + self._finish(self.name) diff --git a/tmdbapis/tmdb.py b/tmdbapis/tmdb.py new file mode 100644 index 0000000..39956e1 --- /dev/null +++ b/tmdbapis/tmdb.py @@ -0,0 +1,1281 @@ +import logging +from datetime import datetime +from typing import Optional, Union, List, Dict + +import requests +from requests import Session + +from tmdbapis import util +from tmdbapis.api3 import API3 +from tmdbapis.api4 import API4 +from tmdbapis.exceptions import Authentication, Invalid +from tmdbapis.objs.pagination import NowPlayingMovies, PopularMovies, TopRatedMovies, UpcomingMovies, Trending, PopularPeople, \ + TVShowsAiringToday, TVShowsOnTheAir, PopularTVShows, TopRatedTVShows, MovieRecommendations, TVShowRecommendations, \ + SearchCompanies, SearchCollections, SearchKeywords, SearchMovies, SearchMulti, SearchPeople, SearchTVShows, \ + CreatedLists, FavoriteMovies, FavoriteTVShows, RatedMovies, RatedTVShows, RatedEpisodes, MovieWatchlist, \ + TVShowWatchlist, DiscoverMovies, DiscoverTVShows +from tmdbapis.objs.pagination import TMDbList +from tmdbapis.objs.reload import Account, Collection, Configuration, Company, Credit, Keyword, Movie, \ + Network, Person, Review, TVShow, Season, Episode, EpisodeGroup +from tmdbapis.objs.simple import Country, CountryCertifications, Genre, WatchProvider, FindResults, Language + +logger = logging.getLogger(__name__) + + +class TMDbAPIs: + """ Main Object Class + + Parameters: + apikey (str): TMDb V3 API Key. + session_id (Optional[str]): TMDb V3 Session ID. + v4_access_token (Optional[str]): TMDb V4 Access Token. + language (str): Default TMDb language. + session (Optional[Session]): Use you're own Session object + + Attributes: + language (str): TMDb Language + account_id (int): TMDb V3 Account ID. + session_id (str): TMDb V3 Session ID. + v4_account_id (str): TMDb V4 Account ID. + v4_access_token (str): TMDb V4 Access Token. + """ + def __init__(self, apikey: str, session_id: Optional[str] = None, v4_access_token: Optional[str] = None, language="en", session: Optional[Session] = None): + self._language = None + self._session = Session() if session is None else session + self._api4 = API4(v4_access_token, session=self._session) if v4_access_token else None + self._api = API3(apikey, session_id=session_id, api4=self._api4, session=self._session, validate=False) + self._request_token = None + self._movie_certifications = None + self._tv_certifications = None + self._movie_genres = None + self._movie_genre_lookups = None + self._tv_genres = None + self._tv_genre_lookups = None + self._provider_regions = None + self._movie_providers = None + self._tv_providers = None + self._config = None + self._config = self.configuration() + self._iso_3166_1 = {v.iso_3166_1: v for v in self._config.countries} + self._iso_639_1 = {v.iso_639_1: v for v in self._config.languages} + self._languages = self._config.primary_translations + [v.iso_639_1 for k, v in self._iso_639_1.items()] + self._image_url = f"{self._config.secure_base_image_url}original" + self.language = language + self._include_language = f"{self.language[:2]},null" if len(self.language) > 2 else "null" + + @property + def _movie_genre_lookup(self): + if self._movie_genre_lookups is None: + self._movie_genre_lookups = {g.id: g for g in self.movie_genres()} + return self._movie_genre_lookups + + @property + def _tv_genre_lookup(self): + if self._tv_genre_lookups is None: + self._tv_genre_lookups = {g.id: g for g in self.tv_genres()} + return self._tv_genre_lookups + + def _get_object(self, lookup, obj_type): + if isinstance(lookup, dict): + if obj_type == "country" and "iso_3166_1" in lookup: + lookup = lookup["iso_3166_1"] + elif obj_type == "language" and "iso_639_1" in lookup: + lookup = lookup["iso_639_1"] + elif obj_type in ["movie_genre", "tv_genre"] and "id" in lookup: + lookup = lookup["id"] + else: + return None + if not lookup: + return None + if obj_type == "country": + return self._iso_3166_1[lookup] + elif obj_type == "language": + return self._iso_639_1[lookup] + elif obj_type == "movie_genre": + return self._movie_genre_lookup[int(lookup)] + elif obj_type == "tv_genre": + return self._tv_genre_lookup[int(lookup)] + + @property + def language(self): + return self._language + + @language.setter + def language(self, language): + if language in self._languages: + self._language = language + else: + raise Invalid(f"Language: {language} Invalid") + + @property + def account_id(self): + return self._api.account_id + + @property + def session_id(self): + return self._api.session_id + + @property + def v4_account_id(self): + return self._v4_check().account_id + + @property + def v4_access_token(self): + return self._v4_check().access_token + + def authenticate(self, username: str, password: str): + """ Use this to authenticate the TMDb V3 Session. + + Parameters: + username (str): TMDb Username. + password (str): TMDb Password. + """ + request_token = self._api.authentication_create_request_token()["request_token"] + self._api.authentication_create_session_with_login(username, password, request_token) + self._api.authentication_create_session(request_token) + + def v4_access(self, access_token: str): + """ Use this method to set up TMDb's V4 API + + To gain read access to TMDb V4's API just provide you're TMDb V4 Access Token either with this method or by using the ``v4_access_token`` Parameter of the :class:`~tmdbapis.tmdb.TMDbAPIs` constructor. + + To gain write access to TMDb V4's API + + 1. Gain Read Access + 2. Authenticate the URL returned from :meth:`v4_authenticate`. + 3. Approve the authentication using :meth:`v4_approved`. + + To get you're TMDb V3 Write Access Token use :attr:`TMDbAPIs.v4_access_token` After it's been approved. + + Parameters: + access_token (str): TMDb V4 Access Token + """ + self._api4 = API4(access_token, session=self._session) + self._api._api4 = self._api4 + + def _v4_check(self, write=False): + if not self._api4: + raise Authentication(f"Requires V4 API Read Access Token, use tmdbapis.v4_access(access_token)") + if write and not self._api4.has_write_token: + raise Authentication(f"Requires V4 API Write Access Token, use tmdbapis.v4_authenticate() and approve the returned URL then use tmdbapis.v4_approved()") + return self._api4 + + def v4_authenticate(self): + """ Use this method to get the authentication URL for write access to TMDb V4 API """ + self._request_token = self._v4_check().auth_create_request_token()["request_token"] + return f"https://www.themoviedb.org/auth/access?request_token={self._request_token}" + + def v4_approved(self): + """ Use this method once the URL from :meth:`v4_authenticate` has been authenticated to gain write access to TMDb V4 API """ + if not self._request_token: + raise Authentication("Requires V4 Authentication, use tmdbapis.v4_authenticate() and approve the returned URL") + self._v4_check().auth_create_access_token(self._request_token) + + def account(self): + """ :class:`~tmdbapis.objs.reload.Account` Object with your account details. + + Returns: + :class:`~tmdbapis.objs.reload.Account` + + Raises: + :class:`~tmdbapis.exceptions.Authentication`: When you haven't authenticated a session yet. + """ + return Account(self) + + def created_lists(self, v3: bool = False): + """ Paginated Object of all the lists created by an account. Will include private lists if you are the owner. + + Parameters: + v3 (bool): Force List V3 Usage + + Returns: + :class:`~tmdbapis.objs.pagination.CreatedLists` + """ + return CreatedLists(self, v3=v3) + + def favorite_movies(self, sort_by: Optional[str] = None, v3: bool = False): + """ Paginated Object of movies you have marked as a favorite. + + Parameters: + sort_by (Optional[str]): How the results are sorted. + v3 (bool): Force List V3 Usage + + .. list-table:: Sort Options + :header-rows: 0 + + * - ``created_at.asc`` + - ``created_at.desc`` + * - ``release_date.asc`` * + - ``release_date.desc`` * + * - ``title.asc`` * + - ``title.desc`` * + * - ``vote_average.asc`` * + - ``vote_average.desc`` * + + \\* V4 Lists Only + + Returns: + :class:`~tmdbapis.objs.pagination.FavoriteMovies` + + Raises: + :class:`~tmdbapis.exceptions.Invalid`: When ``sort_by`` is not a valid option. + """ + return FavoriteMovies(self, sort_by=util.validate_sort(sort_by, v3, True), v3=v3) + + def favorite_tv_shows(self, sort_by: Optional[str] = None, v3: bool = False): + """ Paginated Object of TV shows you have marked as a favorite. + + Parameters: + sort_by (Optional[str]): How the results are sorted. + v3 (bool): Force List V3 Usage + + .. list-table:: Sort Options + :header-rows: 0 + + * - ``created_at.asc`` + - ``created_at.desc`` + * - ``first_air_date.asc`` * + - ``first_air_date.desc`` * + * - ``name.asc`` * + - ``name.desc`` * + * - ``vote_average.asc`` * + - ``vote_average.desc`` * + + \\* V4 Lists Only + + Returns: + :class:`~tmdbapis.objs.pagination.FavoriteTVShows` + + Raises: + :class:`~tmdbapis.exceptions.Invalid`: When ``sort_by`` is not a valid option. + """ + return FavoriteTVShows(self, sort_by=util.validate_sort(sort_by, v3, False), v3=v3) + + def rated_movies(self, sort_by: Optional[str] = None, v3: bool = False): + """ Paginated Object of movies you have rated. + + Parameters: + sort_by (Optional[str]): How the results are sorted. + v3 (bool): Force List V3 Usage + + .. list-table:: Sort Options + :header-rows: 0 + + * - ``created_at.asc`` + - ``created_at.desc`` + * - ``release_date.asc`` * + - ``release_date.desc`` * + * - ``title.asc`` * + - ``title.desc`` * + * - ``vote_average.asc`` * + - ``vote_average.desc`` * + + \\* V4 Lists Only + + Returns: + :class:`~tmdbapis.objs.pagination.RatedMovies` + + Raises: + :class:`~tmdbapis.exceptions.Invalid`: When ``sort_by`` is not a valid option. + """ + return RatedMovies(self, sort_by=util.validate_sort(sort_by, v3, True), v3=v3) + + def rated_tv_shows(self, sort_by: Optional[str] = None, v3: bool = False): + """ Paginated Object of TV shows you have rated. + + Parameters: + sort_by (Optional[str]): How the results are sorted. + v3 (bool): Force List V3 Usage + + .. list-table:: Sort Options + :header-rows: 0 + + * - ``created_at.asc`` + - ``created_at.desc`` + * - ``first_air_date.asc`` * + - ``first_air_date.desc`` * + * - ``name.asc`` * + - ``name.desc`` * + * - ``vote_average.asc`` * + - ``vote_average.desc`` * + + \\* V4 Lists Only + + Returns: + :class:`~tmdbapis.objs.pagination.RatedTVShows` + + Raises: + :class:`~tmdbapis.exceptions.Invalid`: When ``sort_by`` is not a valid option. + """ + return RatedTVShows(self, sort_by=util.validate_sort(sort_by, v3, False), v3=v3) + + def rated_episodes(self, sort_by: Optional[str] = None): + """ Paginated Object of TV episodes you have rated. + + Parameters: + sort_by (Optional[str]): How the results are sorted. + + .. list-table:: Sort Options + :header-rows: 0 + + * - ``created_at.asc`` + - ``created_at.desc`` + + Returns: + :class:`~tmdbapis.objs.pagination.RatedEpisodes` + + Raises: + :class:`~tmdbapis.exceptions.Invalid`: When ``sort_by`` is not a valid option. + """ + return RatedEpisodes(self, sort_by=util.validate_sort(sort_by, True, False)) + + def movie_watchlist(self, sort_by: Optional[str] = None, v3: bool = False): + """ Paginated Object of movies you have added to your watchlist. + + Parameters: + sort_by (Optional[str]): How the results are sorted. + v3 (bool): Force List V3 Usage + + .. list-table:: Sort Options + :header-rows: 0 + + * - ``created_at.asc`` + - ``created_at.desc`` + * - ``release_date.asc`` * + - ``release_date.desc`` * + * - ``title.asc`` * + - ``title.desc`` * + * - ``vote_average.asc`` * + - ``vote_average.desc`` * + + \\* V4 Lists Only + + Returns: + :class:`~tmdbapis.objs.pagination.MovieWatchlist` + + Raises: + :class:`~tmdbapis.exceptions.Invalid`: When ``sort_by`` is not a valid option. + """ + return MovieWatchlist(self, sort_by=util.validate_sort(sort_by, v3, True), v3=v3) + + def tv_show_watchlist(self, sort_by: Optional[str] = None, v3: bool = False): + """ Paginated Object of TV shows you have added to your watchlist. + + Parameters: + sort_by (Optional[str]): How the results are sorted. + v3 (bool): Force List V3 Usage + + .. list-table:: Sort Options + :header-rows: 0 + + * - ``created_at.asc`` + - ``created_at.desc`` + * - ``first_air_date.asc`` * + - ``first_air_date.desc`` * + * - ``name.asc`` * + - ``name.desc`` * + * - ``vote_average.asc`` * + - ``vote_average.desc`` * + + \\* V4 Lists Only + + Returns: + :class:`~tmdbapis.objs.reload.TVShowWatchlist` + + Raises: + :class:`~tmdbapis.exceptions.Invalid`: When ``sort_by`` is not a valid option. + """ + return TVShowWatchlist(self, sort_by=util.validate_sort(sort_by, v3, False), v3=v3) + + def movie_recommendations(self, sort_by: Optional[str] = None): + """ Paginated Object of your personal movie recommendations. (V4 Lists Only) + + Parameters: + sort_by (Optional[str]): How the results are sorted. + + .. list-table:: Sort Options + :header-rows: 0 + + * - ``created_at.asc`` + - ``created_at.desc`` + * - ``release_date.asc`` + - ``release_date.desc`` + * - ``title.asc`` + - ``title.desc`` + * - ``vote_average.asc`` + - ``vote_average.desc`` + + Returns: + :class:`~tmdbapis.objs.pagination.MovieRecommendations` + + Raises: + :class:`~tmdbapis.exceptions.Invalid`: When ``sort_by`` is not a valid option. + """ + return MovieRecommendations(self, sort_by=util.validate_sort(sort_by, False, True)) + + def tv_show_recommendations(self, sort_by: Optional[str] = None): + """ Paginated Object of your personal TV show recommendations. (V4 Lists Only) + + Parameters: + sort_by (Optional[str]): How the results are sorted. + + .. list-table:: Sort Options + :header-rows: 0 + + * - ``created_at.asc`` + - ``created_at.desc`` + * - ``first_air_date.asc`` + - ``first_air_date.desc`` + * - ``name.asc`` + - ``name.desc`` + * - ``vote_average.asc`` + - ``vote_average.desc`` + + Returns: + :class:`~tmdbapis.objs.reload.TVShowRecommendations` + + Raises: + :class:`~tmdbapis.exceptions.Invalid`: When ``sort_by`` is not a valid option. + """ + return TVShowRecommendations(self, sort_by=util.validate_sort(sort_by, False, False)) + + def logout(self): + """ End all V3 and V4 Authenticated Sessions """ + if self._api._session_id: + self._api.authentication_delete_session(self._api._session_id) + if self._api4: + if self._api4.has_write_token: + self._api4.auth_delete_access_token(self._api4.access_token) + + def movie_certifications(self, reload: bool = False) -> Dict[str, CountryCertifications]: + """ Get an up to date list of the officially supported movie certifications on TMDB. + + Parameters: + reload (bool): Reload the cached movie certifications + + Returns: + Dict[str, :class:`~tmdbapis.objs.simple.CountryCertifications`] + """ + if reload or self._movie_certifications is None: + self._movie_certifications = { + k: CountryCertifications(self, v, k) for k, v in + self._api.certifications_get_movie_certifications()["certifications"].items() + } + return self._movie_certifications + + def tv_certifications(self, reload: bool = False) -> Dict[str, CountryCertifications]: + """ Get an up to date list of the officially supported TV show certifications on TMDB. + + Parameters: + reload (bool): Reload the cached tv certifications + + Returns: + Dict[str, :class:`~tmdbapis.objs.simple.CountryCertifications`] + """ + if reload or self._tv_certifications is None: + self._tv_certifications = { + k: CountryCertifications(self, v, k) for k, v in + self._api.certifications_get_tv_certifications()["certifications"].items() + } + return self._tv_certifications + + def movie_change_list(self, + start_date: Optional[Union[datetime, str]] = None, + end_date: Optional[Union[datetime, str]] = None + ) -> List[Movie]: + """ Get a list of :class:`~tmdbapis.objs.reload.Movie` that have been changed in the past 24 hours. + + You can query it for up to 14 days worth of changed Movies at a time with the ``start_date`` and ``end_date`` + query parameters. + + Parameters: + start_date (Optional[Union[datetime, str]]): Filter the results with a start date. Format: YYYY-MM-DD + end_date (Optional[Union[datetime, str]]): Filter the results with an end date. Format: YYYY-MM-DD + + Returns: + List[:class:`~tmdbapis.objs.reload.Movie`] + + Raises: + :class:`~tmdbapis.exceptions.Invalid`: When ``start_date`` or ``end_date`` is in an incorrect format. + """ + return [Movie(self, data) for data in self._api.changes_get_movie_change_list( + start_date=util.validate_date(start_date), + end_date=util.validate_date(end_date) + )["results"]] + + def tv_change_list(self, + start_date: Optional[Union[datetime, str]] = None, + end_date: Optional[Union[datetime, str]] = None + ) -> List[TVShow]: + """ Get a list of :class:`~tmdbapis.objs.reload.TV` that have been changed in the past 24 hours. + + You can query it for up to 14 days worth of changed Shows at a time with the ``start_date`` and ``end_date`` + query parameters. + + Parameters: + start_date (Optional[Union[datetime, str]]): Filter the results with a start date. Format: YYYY-MM-DD + end_date (Optional[Union[datetime, str]]): Filter the results with an end date. Format: YYYY-MM-DD + + Returns: + List[:class:`~tmdbapis.objs.reload.TVShow`] + + Raises: + :class:`~tmdbapis.exceptions.Invalid`: When ``start_date`` or ``end_date`` is in an incorrect format. + """ + return [TVShow(self, data) for data in self._api.changes_get_tv_change_list( + start_date=util.validate_date(start_date), + end_date=util.validate_date(end_date) + )["results"]] + + def person_change_list(self, + start_date: Optional[Union[datetime, str]] = None, + end_date: Optional[Union[datetime, str]] = None + ) -> List[Person]: + """ Get a list of :class:`~tmdbapis.objs.pagination.Person` that have been changed in the past 24 hours. + + You can query it for up to 14 days worth of changed People at a time with the ``start_date`` and ``end_date`` + query parameters. + + Parameters: + start_date (Optional[Union[datetime, str]]): Filter the results with a start date. Format: YYYY-MM-DD + end_date (Optional[Union[datetime, str]]): Filter the results with an end date. Format: YYYY-MM-DD + + Returns: + List[:class:`~tmdbapis.objs.pagination.Person`] + + Raises: + :class:`~tmdbapis.exceptions.Invalid`: When ``start_date`` or ``end_date`` is in an incorrect format. + """ + return [Person(self, data) for data in self._api.changes_get_person_change_list( + start_date=util.validate_date(start_date), + end_date=util.validate_date(end_date) + )["results"]] + + def collection(self, collection_id: int, load: bool = True) -> Collection: + """ Gets the :class:`~tmdbapis.objs.reload.Collection` for the given id. + + Parameters: + collection_id (int): Collection ID of the collection you want. + load (bool): Load the data on creation. + + Returns: + :class:`~tmdbapis.objs.reload.Collection` + + Raises: + :class:`~tmdbapis.exceptions.NotFound`: When no collection is found for the given id. + """ + return Collection(self, {"id": collection_id}, load=load) + + def company(self, company_id: int, load: bool = True) -> Company: + """ Gets the :class:`~tmdbapis.objs.reload.Company` for the given id. + + Parameters: + company_id (int): Company ID of the company you want. + load (bool): Load the data on creation. + + Returns: + :class:`~tmdbapis.objs.reload.Company` + + Raises: + :class:`~tmdbapis.exceptions.NotFound`: When no company is found for the given id. + """ + return Company(self, {"id": company_id}, load=load) + + def configuration(self, reload: bool = False) -> Configuration: + """ Gets the TMDb :class:`~tmdbapis.objs.reload.Configuration`. + + Parameters: + reload (bool): Reload the cached :class:`~tmdbapis.objs.reload.Configuration`. + + Returns: + :class:`~tmdbapis.objs.reload.Configuration` + """ + if reload or self._config is None: + self._config = Configuration(self) + return self._config + + def credit(self, credit_id: str, load: bool = True) -> Credit: + """ Gets the :class:`~tmdbapis.objs.reload.Credit` for the given id. + + Parameters: + credit_id (str): Credit ID of the credit you want. + load (bool): Load the data on creation. + + Returns: + :class:`~tmdbapis.objs.reload.Credit` + + Raises: + :class:`~tmdbapis.exceptions.NotFound`: When no credit is found for the given id. + """ + return Credit(self, {"id": credit_id, "person": {}}, load=load) + + def discover_movies(self, **kwargs) -> DiscoverMovies: + """ Discover movies by different types of data like average rating, number of votes, genres and certifications. + You can get a valid list of certifications from the :meth:`movie_certifications` method. + + Discover also supports a nice list of sort options. See below for all of the available options. + + Please note, when using ``certification`` \\ ``certification.lte`` you must also specify + ``certification_country``. These two parameters work together in order to filter the results. You can only + filter results with the countries added to :meth:`movie_certifications`. + + If you specify the ``region`` parameter, the regional release date will be used instead of the primary + release date. The date returned will be the first date based on your query (ie. if a ``with_release_type`` + is specified). It's important to note the order of the release types that are used. Specifying "2|3" would + return the limited theatrical release date as opposed to "3|2" which would return the theatrical date. + + Also note that a number of filters support being comma (``,``) or pipe (``|``) separated. Comma's are + treated like an ``AND`` and query while pipe's are an ``OR``. + + ``.`` cannot be included directly in the function parameters so the parameters must be provided as a + kwargs dictionary. + + Parameters: + language (Optional[str]): ISO-639-1 or ISO-3166-1 value to display translated data for the fields that support it. + region (Optional[str]): ISO-3166-1 code to filter release dates. Must be uppercase. + sort_by (Optional[str]): Allowed Values: ``popularity.asc``, ``popularity.desc``, ``release_date.asc``, ``release_date.desc``, ``revenue.asc``, ``revenue.desc``, ``primary_release_date.asc``, ``primary_release_date.desc``, ``original_title.asc``, ``original_title.desc``, ``vote_average.asc``, ``vote_average.desc``, ``vote_count.asc``, ``vote_count.desc`` + certification_country (Optional[str]): Used in conjunction with the ``certification`` filter, use this to specify a country with a valid certification. + certification (Optional[str]): Filter results with a valid certification from the ``certification_country`` field. + certification.lte (Optional[str]): Filter and only include movies that have a certification that is less than or equal to the specified value. + certification.gte (Optional[str]): Filter and only include movies that have a certification that is greater than or equal to the specified value. + include_adult (Optional[bool]): A filter and include or exclude adult movies. + include_video (Optional[bool]): A filter to include or exclude videos. + page (Optional[int]): Specify the page of results to query. + primary_release_year (Optional[int): A filter to limit the results to a specific primary release year. + primary_release_date.gte (Optional[str]): Filter and only include movies that have a primary release date that is greater or equal to the specified value. Format: YYYY-MM-DD + primary_release_date.lte (Optional[str]): Filter and only include movies that have a primary release date that is less than or equal to the specified value. Format: YYYY-MM-DD + release_date.gte (Optional[str]): Filter and only include movies that have a release date (looking at all release dates) that is greater or equal to the specified value. Format: YYYY-MM-DD + release_date.lte (Optional[str]): Filter and only include movies that have a release date (looking at all release dates) that is less than or equal to the specified value. Format: YYYY-MM-DD + with_release_type (Optional[int]): Specify a comma (AND) or pipe (OR) separated value to filter release types by. These release types map to the same values found on the movie release date method. + year (Optional[int]): A filter to limit the results to a specific year (looking at all release dates). + vote_count.gte (Optional[int]): Filter and only include movies that have a vote count that is greater or equal to the specified value. + vote_count.lte (Optional[int]): Filter and only include movies that have a vote count that is less than or equal to the specified value. + vote_average.gte (Optional[float]): Filter and only include movies that have a rating that is greater or equal to the specified value. + vote_average.lte (Optional[float]): Filter and only include movies that have a rating that is less than or equal to the specified value. + with_cast (Optional[str]): A comma separated list of person ID's. Only include movies that have one of the ID's added as an actor. + with_crew (Optional[str]): A comma separated list of person ID's. Only include movies that have one of the ID's added as a crew member. + with_people (Optional[str]): A comma separated list of person ID's. Only include movies that have one of the ID's added as a either a actor or a crew member. + with_companies (Optional[str]): A comma separated list of production company ID's. Only include movies that have one of the ID's added as a production company. + with_genres (Optional[str]): Comma separated value of genre ids that you want to include in the results. + without_genres (Optional[str]): Comma separated value of genre ids that you want to exclude from the results. + with_keywords (Optional[str]): A comma separated list of keyword ID's. Only includes movies that have one of the ID's added as a keyword. + without_keywords (Optional[str]): Exclude items with certain keywords. You can comma and pipe separate these values to create an 'AND' or 'OR' logic. + with_runtime.gte (Optional[int]): Filter and only include movies that have a runtime that is greater or equal to a value. + with_runtime.lte (Optional[int]): Filter and only include movies that have a runtime that is less than or equal to a value..000 + with_original_language (Optional[str]): Specify an ISO 639-1 string to filter results by their original language value. + with_watch_providers (Optional[str]): A comma or pipe separated list of watch provider ID's. Combine this filter with ``watch_region`` in order to filter your results by a specific watch provider in a specific region. + watch_region (Optional[str]): An ISO 3166-1 code. Combine this filter with ``with_watch_providers`` in order to filter your results by a specific watch provider in a specific region. + with_watch_monetization_types (Optional[str]): In combination with ``watch_region``, you can filter by monetization type. Allowed Values: ``flatrate``, ``free``, ``ads``, ``rent``, ``buy`` + + Returns: + :class:`~tmdbapis.objs.pagination.DiscoverMovies` + + Raises: + :class:`~tmdbapis.exceptions.Invalid`: When one of the attributes given is Invalid. + """ + return DiscoverMovies(self, **util.validate_discover(True, **kwargs)) + + def discover_tv_shows(self, **kwargs) -> DiscoverTVShows: + """ Discover TV shows by different types of data like average rating, number of votes, genres, the network they + aired on and air dates. + + Discover also supports a nice list of sort options. See below for all of the available options. + + Also note that a number of filters support being comma (``,``) or pipe (``|``) separated. Comma's are + treated like an ``AND`` and query while pipe's are an ``OR``. + + ``.`` cannot be included directly in the function parameters so the parameters must be provided as a + kwargs dictionary. + + Parameters: + language (Optional[str]): ISO-639-1 or ISO-3166-1 value to display translated data for the fields that support it. + sort_by (Optional[str]): Allowed Values: ``vote_average.desc``, ``vote_average.asc``, ``first_air_date.desc``, ``first_air_date.asc``, ``popularity.desc``, ``popularity.asc`` + air_date.gte (Optional[str]): Filter and only include TV shows that have a air date (by looking at all episodes) that is greater or equal to the specified value. Format: YYYY-MM-DD + air_date.lte (Optional[str]): Filter and only include TV shows that have a air date (by looking at all episodes) that is less than or equal to the specified value. Format: YYYY-MM-DD + first_air_date.gte (Optional[str]): Filter and only include TV shows that have a original air date that is greater or equal to the specified value. Can be used in conjunction with the ``include_null_first_air_dates`` filter if you want to include items with no air date. Format: YYYY-MM-DD + first_air_date.lte (Optional[str]): Filter and only include TV shows that have a original air date that is less than or equal to the specified value. Can be used in conjunction with the ``include_null_first_air_dates`` filter if you want to include items with no air date. Format: YYYY-MM-DD + first_air_date_year (Optional[int]): Filter and only include TV shows that have a original air date year that equal to the specified value. Can be used in conjunction with the ``include_null_first_air_dates`` filter if you want to include items with no air date. + page (Optional[int]): Specify the page of results to query. + timezone (Optional[str]): Used in conjunction with the ``air_date.gte``/``air_date.lte`` filter to calculate the proper UTC offset. + vote_average.gte (Optional[float]): Filter and only include TV shows that have a rating that is greater or equal to the specified value. + vote_average.lte (Optional[float]): Filter and only include TV shows that have a rating that is less than or equal to the specified value. + vote_count.gte (Optional[int]): Filter and only include TV shows that have a vote count that is greater or equal to the specified value. + vote_count.lte (Optional[int]): Filter and only include TV shows that have a vote count that is less than or equal to the specified value. + with_genres (Optional[str]): Comma separated value of genre ids that you want to include in the results. + with_networks (Optional[str]): Comma separated value of network ids that you want to include in the results. + without_genres (Optional[str]): Comma separated value of genre ids that you want to exclude from the results. + with_runtime.gte (Optional[int]): Filter and only include TV shows with an episode runtime that is greater than or equal to a value. + with_runtime.lte (Optional[int]): Filter and only include TV shows with an episode runtime that is less than or equal to a value. + include_null_first_air_dates (Optional[bool]): Use this filter to include TV shows that don't have an air date while using any of the ``first_air_date`` filters. + with_original_language (Optional[str]): Specify an ISO 639-1 string to filter results by their original language value. + without_keywords (Optional[str]): Exclude items with certain keywords. You can comma and pipe separate these values to create an 'AND' or 'OR' logic. + screened_theatrically (Optional[bool]): Filter results to include items that have been screened theatrically. + with_companies (Optional[str]): A comma separated list of production company ID's. Only include movies that have one of the ID's added as a production company. + with_keywords (Optional[str]): A comma separated list of keyword ID's. Only includes TV shows that have one of the ID's added as a keyword. + with_watch_providers (Optional[str]): A comma or pipe separated list of watch provider ID's. Combine this filter with ``watch_region`` in order to filter your results by a specific watch provider in a specific region. + watch_region (Optional[str]): An ISO 3166-1 code. Combine this filter with ``with_watch_providers`` in order to filter your results by a specific watch provider in a specific region. + with_watch_monetization_types (Optional[str]): In combination with ``watch_region``, you can filter by monetization type. Allowed Values: ``flatrate``, ``free``, ``ads``, ``rent``, ``buy`` + + Returns: + :class:`~tmdbapis.objs.pagination.DiscoverTVShows` + + Raises: + :class:`~tmdbapis.exceptions.Invalid`: When one of the attributes given is Invalid. + """ + return DiscoverTVShows(self, **util.validate_discover(False, **kwargs)) + + def find_by_id(self, imdb_id: Optional[str] = None, freebase_mid: Optional[str] = None, freebase_id: Optional[str] = None, + tvdb_id: Optional[str] = None, tvrage_id: Optional[str] = None, facebook_id: Optional[str] = None, + twitter_id: Optional[str] = None, instagram_id: Optional[str] = None) -> FindResults: + """ Gets the :class:`~tmdbapis.objs.simple.FindResults` for the given external id. + + Parameters: + imdb_id (str): IMDb ID to find. + freebase_mid (str): Freebase MID to find. + freebase_id (str): Freebase ID to find. + tvdb_id (str): TVDb ID to find. + tvrage_id (str): TVRage ID to find. + facebook_id (str): Facebook ID to find. + twitter_id (str): Twitter ID to find. + instagram_id (str): Instagram ID to find. + + Returns: + :class:`~tmdbapis.objs.simple.FindResults` + + Raises: + :class:`~tmdbapis.exceptions.NotFound`: When no results are returned. + :class:`~tmdbapis.exceptions.Invalid`: When no external id is given. + """ + if imdb_id: + return FindResults(self, imdb_id, "imdb_id") + elif freebase_mid: + return FindResults(self, freebase_mid, "freebase_mid") + elif freebase_id: + return FindResults(self, freebase_id, "freebase_id") + elif tvdb_id: + return FindResults(self, tvdb_id, "tvdb_id") + elif tvrage_id: + return FindResults(self, tvrage_id, "tvrage_id") + elif facebook_id: + return FindResults(self, facebook_id, "facebook_id") + elif twitter_id: + return FindResults(self, twitter_id, "twitter_id") + elif instagram_id: + return FindResults(self, instagram_id, "instagram_id") + else: + raise Invalid("At least one ID is required") + + def movie_genres(self, reload: bool = False) -> List[Genre]: + """ Gets a list of all movie :class:`~tmdbapis.objs.simple.Genre`. + + Parameters: + reload (bool): Reload the cached movie genres. + + Returns: + List[:class:`~tmdbapis.objs.simple.Genre`] + """ + if reload or self._movie_genres is None: + self._movie_genres = [Genre(self, g) for g in self._api.genres_get_movie_list()["genres"]] + return self._movie_genres + + def tv_genres(self, reload: bool = False) -> List[Genre]: + """ Gets a list of all TV show :class:`~tmdbapis.objs.simple.Genre`. + + Parameters: + reload (bool): Reload the cached movie genres. + + Returns: + List[:class:`~tmdbapis.objs.simple.Genre`] + """ + if reload or self._tv_genres is None: + self._tv_genres = [Genre(self, g) for g in self._api.genres_get_tv_list()["genres"]] + return self._tv_genres + + def keyword(self, keyword_id: int, load: bool = True) -> Keyword: + """ Gets the :class:`~tmdbapis.objs.reload.Keyword` for the given id. + + Parameters: + keyword_id (int): Keyword ID of the keyword you want. + load (bool): Load the data on creation. + + Returns: + :class:`~tmdbapis.objs.reload.Keyword` + + Raises: + :class:`~tmdbapis.exceptions.NotFound`: When no keyword is found for the given id. + """ + return Keyword(self, {"id": keyword_id}, load=load) + + def list(self, list_id: int, load: bool = True) -> TMDbList: + """ Gets the :class:`~tmdbapis.objs.pagination.TMDbList` for the given id. + + Parameters: + list_id (str): Keyword ID of the list you want. + load (bool): Load the data on creation. + + Returns: + :class:`~tmdbapis.objs.pagination.TMDbList` + + Raises: + :class:`~tmdbapis.exceptions.NotFound`: When no list is found for the given id. + """ + return TMDbList(self, {"id": list_id}, load=load) + + def create_list(self, name: str, iso_639_1: Union[Language, str], description: Optional[str] = "", + public: bool = True, iso_3166_1: Optional[Union[Country, str]] = None, load: bool = True) -> Union[TMDbList, int]: + """ Creates a new List on TMDb and returns either a :class:`~tmdbapis.objs.pagination.TMDbList` Object or the List ID. + + Parameters: + name (str): Name of the List. + iso_639_1 (Union[Language, str]): ISO 639-1 Language Code of the List or :class:`tmdbapis.objs.simple.Language` Object. + description (Optional[str]): Description of the List. + public (bool): Determine if the list is a public list. (V4 Lists Only) + iso_3166_1 (Optional[Union[Country, str]]): ISO 3166-1 Alpha-2 Country Code of the List or :class:`tmdbapis.objs.simple.Country` Object. (V4 Lists Only) + load (bool): Load the list to return after creating it or just return the created List ID. + + Returns: + Union[:class:`~tmdbapis.objs.pagination.TMDbList`, int] + """ + if self._api4 and self._api4.has_write_token: + list_id = self._v4_check(write=True).list_create_list( + name, + util.validate_language(iso_639_1, self._iso_639_1), + description=description, + public=public, + iso_3166_1=util.validate_country(iso_3166_1, self._iso_3166_1) + )["id"] + else: + list_id = self._api.lists_create_list( + name=name, description=description, language=util.validate_language(iso_639_1, self._iso_639_1) + )["list_id"] + return self.list(list_id) if load else int(list_id) + + def movie(self, movie_id: int, load: bool = True) -> Movie: + """ Gets the :class:`~tmdbapis.objs.reload.Movie` for the given id. + + Parameters: + movie_id (str): Movie ID of the movie you want. + load (bool): Load the data on creation. + + Returns: + :class:`~tmdbapis.objs.reload.Movie` + + Raises: + :class:`~tmdbapis.exceptions.NotFound`: When no keyword is found for the given id. + """ + return Movie(self, {"id": movie_id}, load=load) + + def latest_movie(self) -> Movie: + """ Gets the latest :class:`~tmdbapis.objs.reload.Movie` added on TMDb. + + Returns: + :class:`~tmdbapis.objs.reload.Movie` + """ + return Movie(self, self._api.movies_get_latest(language=self.language)) + + def now_playing_movies(self, region: Optional[Union[Country, str]] = None) -> NowPlayingMovies: + """ Paginated Object of Movies Now playing in theaters. + + Parameters: + region (Optional[Union[Country, str]]): ISO 3166-1 Alpha-2 Country Code or :class:`tmdbapis.objs.simple.Country` Object to narrow the search to only look for theatrical release dates within the specified country. + + Returns: + :class:`~tmdbapis.objs.pagination.NowPlayingMovies` + + Raises: + :class:`~tmdbapis.exceptions.Invalid`: When the Country provided is not valid. + """ + return NowPlayingMovies(self, region=util.validate_country(region, self._iso_3166_1)) + + def popular_movies(self, region: Optional[Union[Country, str]] = None) -> PopularMovies: + """ Paginated Object of Popular Movies on TMDb. + + Parameters: + region (Optional[Union[Country, str]]): ISO 3166-1 Alpha-2 Country Code or :class:`tmdbapis.objs.simple.Country` Object to narrow the search to only look for theatrical release dates within the specified country. + + Returns: + :class:`~tmdbapis.objs.pagination.PopularMovies` + + Raises: + :class:`~tmdbapis.exceptions.Invalid`: When the Country provided is not valid. + """ + return PopularMovies(self, region=util.validate_country(region, self._iso_3166_1)) + + def top_rated_movies(self, region: Optional[Union[Country, str]] = None) -> TopRatedMovies: + """ Paginated Object of the Top Rated Movies on TMDb. + + Parameters: + region (Optional[Union[Country, str]]): ISO 3166-1 Alpha-2 Country Code or :class:`tmdbapis.objs.simple.Country` Object to narrow the search to only look for theatrical release dates within the specified country. + + Returns: + :class:`~tmdbapis.objs.pagination.TopRatedMovies` + + Raises: + :class:`~tmdbapis.exceptions.Invalid`: When the Country provided is not valid. + """ + return TopRatedMovies(self, region=util.validate_country(region, self._iso_3166_1)) + + def upcoming_movies(self, region: Optional[Union[Country, str]] = None) -> UpcomingMovies: + """ Paginated Object of Upcoming Movies. + + Parameters: + region (Optional[Union[Country, str]]): ISO 3166-1 Alpha-2 Country Code or :class:`tmdbapis.objs.simple.Country` Object to narrow the search to only look for theatrical release dates within the specified country. + + Returns: + :class:`~tmdbapis.objs.pagination.UpcomingMovies` + + Raises: + :class:`~tmdbapis.exceptions.Invalid`: When the Country provided is not valid. + """ + return UpcomingMovies(self, region=util.validate_country(region, self._iso_3166_1)) + + def network(self, network_id: int, load: bool = True) -> Network: + """ Gets the :class:`~tmdbapis.objs.reload.Network` for the given id. + + Parameters: + network_id (int): Network ID of the network you want. + load (bool): Load the data on creation. + + Returns: + :class:`~tmdbapis.objs.reload.Network` + + Raises: + :class:`~tmdbapis.exceptions.NotFound`: When no network is found for the given id. + """ + return Network(self, {"id": network_id}, load=load) + + def trending(self, media_type: str, time_window: str) -> Trending: + """ Gets the :class:`~tmdbapis.objs.pagination.Trending` for the given id. + + Parameters: + media_type (str): Trending media type. Allowed Values: ``all``, ``movie``, ``tv``, and ``person`` + time_window (str): Trending list time window. Allowed Values: ``day`` and ``week`` + + Returns: + :class:`~tmdbapis.objs.pagination.Trending` + + Raises: + :class:`~tmdbapis.exceptions.Invalid`: When ``media_type`` or ``time_window`` is given an invalid option. + """ + if media_type not in ["all", "movie", "tv", "person"]: + raise Invalid(f"media_type: {media_type} Invalid. Options: all, movie, tv, or person") + if time_window not in ["day", "week"]: + raise Invalid(f"time_window: {time_window} Invalid. Options: day or week") + return Trending(self, media_type, time_window) + + def person(self, person_id: int, load: bool = True) -> Person: + """ Gets the :class:`~tmdbapis.objs.reload.Person` for the given id. + + Parameters: + person_id (int): Person ID of the person you want. + load (bool): Load the data on creation. + + Returns: + :class:`~tmdbapis.objs.reload.Person` + + Raises: + :class:`~tmdbapis.exceptions.NotFound`: When no person is found for the given id. + """ + return Person(self, {"id": person_id}, load=load) + + def latest_person(self) -> Person: + """ Gets the latest :class:`~tmdbapis.objs.reload.Person` added on TMDb. + + Returns: + :class:`~tmdbapis.objs.reload.Person` + """ + return Person(self, self._api.people_get_latest(language=self.language)) + + def popular_people(self) -> PopularPeople: + """ Paginated Object of Popular People on TMDb. + + Returns: + :class:`~tmdbapis.objs.pagination.PopularPeople` + """ + return PopularPeople(self) + + def review(self, review_id: str, load: bool = True) -> Review: + """ Gets the :class:`~tmdbapis.objs.reload.Review` for the given id. + + Parameters: + review_id (str): Review ID of the review you want. + load (bool): Load the data on creation. + + Returns: + :class:`~tmdbapis.objs.reload.Review` + + Raises: + :class:`~tmdbapis.exceptions.NotFound`: When no review is found for the given id. + """ + return Review(self, {"id": review_id}, load=load) + + def company_search(self, query: str) -> SearchCompanies: + """ Searches TMDb for companies. + + Parameters: + query (str): Query to search for. + + Returns: + :class:`~tmdbapis.objs.pagination.SearchCompanies` + + Raises: + :class:`~tmdbapis.exceptions.NotFound`: When no results are found for the search. + """ + return SearchCompanies(self, query) + + def collection_search(self, query: str) -> SearchCollections: + """ Searches TMDb for collections. + + Parameters: + query (str): Query to search for. + + Returns: + :class:`~tmdbapis.objs.pagination.SearchCollections` + + Raises: + :class:`~tmdbapis.exceptions.NotFound`: When no results are found for the search. + """ + return SearchCollections(self, requests.utils.quote(query)) + + def keyword_search(self, query: str) -> SearchKeywords: + """ Searches TMDb for keywords. + + Parameters: + query (str): Query to search for. + + Returns: + :class:`~tmdbapis.objs.pagination.SearchKeywords` + + Raises: + :class:`~tmdbapis.exceptions.NotFound`: When no results are found for the search. + """ + return SearchKeywords(self, requests.utils.quote(query)) + + def movie_search(self, query: str, include_adult: Optional[bool] = None, + region: Optional[Union[Country, str]] = None, year: Optional[int] = None, + primary_release_year: Optional[int] = None) -> SearchMovies: + """ Searches TMDb for movies. + + Parameters: + query (str): Query to search for. + include_adult (Optional[bool]): Choose whether to include adult (pornography) content in the results. + region (Optional[Union[Country, str]]): Specify a ISO 3166-1 code or :class:`tmdbapis.objs.simple.Country` Object to filter release dates. Must be uppercase. + year (Optional[int]): Specify a year for the search. + primary_release_year (Optional[int]): Specify a primary release year for the search. + + Returns: + :class:`~tmdbapis.objs.pagination.SearchMovies` + + Raises: + :class:`~tmdbapis.exceptions.NotFound`: When no results are found for the search. + """ + return SearchMovies(self, requests.utils.quote(query), include_adult=include_adult, + region=util.validate_country(region, self._iso_3166_1), + year=year, primary_release_year=primary_release_year) + + def multi_search(self, query: str, include_adult: Optional[bool] = None, + region: Optional[Union[Country, str]] = None) -> SearchMulti: + """ Searches TMDb for movies, tv shows, and people. + + Parameters: + query (str): Query to search for. + include_adult (Optional[bool]): Choose whether to include adult (pornography) content in the results. + region (Optional[Union[Country, str]]): Specify a ISO 3166-1 code or :class:`tmdbapis.objs.simple.Country` Object to filter release dates. Must be uppercase. + + Returns: + :class:`~tmdbapis.objs.pagination.SearchMulti` + + Raises: + :class:`~tmdbapis.exceptions.NotFound`: When no results are found for the search. + """ + return SearchMulti(self, requests.utils.quote(query), include_adult=include_adult, + region=util.validate_country(region, self._iso_3166_1)) + + def people_search(self, query: str, include_adult: Optional[bool] = None, + region: Optional[Union[Country, str]] = None) -> SearchPeople: + """ Searches TMDb for people. + + Parameters: + query (str): Query to search for. + include_adult (Optional[bool]): Choose whether to include adult (pornography) content in the results. + region (Optional[Union[Country, str]]): Specify a ISO 3166-1 code or :class:`tmdbapis.objs.simple.Country` Object to filter release dates. Must be uppercase. + + Returns: + :class:`~tmdbapis.objs.pagination.SearchPeople` + + Raises: + :class:`~tmdbapis.exceptions.NotFound`: When no results are found for the search. + """ + return SearchPeople(self, requests.utils.quote(query), include_adult=include_adult, + region=util.validate_country(region, self._iso_3166_1)) + + def tv_search(self, query: str, include_adult: Optional[bool] = None, + first_air_date_year: Optional[int] = None) -> SearchTVShows: + """ Searches TMDb for tv shows. + + Parameters: + query (str): Query to search for. + include_adult (Optional[bool]): Choose whether to include adult (pornography) content in the results. + first_air_date_year (Optional[int]): Specify a first air date year for the search. + + Returns: + :class:`~tmdbapis.objs.pagination.SearchTVShows` + + Raises: + :class:`~tmdbapis.exceptions.NotFound`: When no results are found for the search. + """ + return SearchTVShows(self, requests.utils.quote(query), include_adult=include_adult, + first_air_date_year=first_air_date_year) + + def tv_show(self, tv_id: int, load: bool = True) -> TVShow: + """ Gets the :class:`~tmdbapis.objs.reload.TVShow` for the given id. + + Parameters: + tv_id (int): TV ID of the show you want. + load (bool): Load the data on creation. + + Returns: + :class:`~tmdbapis.objs.reload.TVShow` + + Raises: + :class:`~tmdbapis.exceptions.NotFound`: When no keyword is found for the given id. + """ + return TVShow(self, {"id": tv_id}, load=load) + + def latest_tv(self) -> TVShow: + """ Gets the latest :class:`~tmdbapis.objs.reload.TVShow` added on TMDb. + + Returns: + :class:`~tmdbapis.objs.reload.TVShow` + """ + return TVShow(self, self._api.tv_get_latest(language=self.language)) + + def tv_airing_today(self) -> TVShowsAiringToday: + """ Paginated Object of TV Shows Airing Today. + + Returns: + :class:`~tmdbapis.objs.reload.TVShowsAiringToday` + """ + return TVShowsAiringToday(self) + + def tv_on_the_air(self) -> TVShowsOnTheAir: + """ Paginated Object of TV Shows On the Air. + + Returns: + :class:`~tmdbapis.objs.reload.TVShowsOnTheAir` + """ + return TVShowsOnTheAir(self) + + def popular_tv(self) -> PopularTVShows: + """ Paginated Object of Popular TV Shows. + + Returns: + :class:`~tmdbapis.objs.pagination.PopularTVShows` + """ + return PopularTVShows(self) + + def top_rated_tv(self) -> TopRatedTVShows: + """ Paginated Object of Top Rated TV Shows On. + + Returns: + :class:`~tmdbapis.objs.reload.TVShowsOnTheAir` + """ + return TopRatedTVShows(self) + + def tv_season(self, tv_id: int, season_number: int, load: bool = True) -> Season: + """ Gets the :class:`~tmdbapis.objs.reload.Movie` for the given id. + + Parameters: + tv_id (int): TV ID of the show the contains the season you want. + season_number (int): Season number to grab. + load (bool): Load the data on creation. + + Returns: + :class:`~tmdbapis.objs.reload.Season` + + Raises: + :class:`~tmdbapis.exceptions.NotFound`: When no season is found for the given id. + """ + return Season(self, {"season_number": season_number}, tv_id, load=load) + + def tv_episode(self, tv_id: int, season_number: int, episode_number: int, load: bool = True) -> Episode: + """ Gets the :class:`~tmdbapis.objs.reload.Episode` for the given id. + + Parameters: + tv_id (int): TV ID of the show the contains the season you want. + season_number (int): Season number to grab. + episode_number (int): Episode number to grab. + load (bool): Load the data on creation. + + Returns: + :class:`~tmdbapis.objs.reload.Episode` + + Raises: + :class:`~tmdbapis.exceptions.NotFound`: When no keyword is found for the given id. + """ + return Episode(self, {"season_number": season_number, "episode_number": episode_number}, tv_id, load=load) + + def episode_group(self, episode_group_id: str, load: bool = True) -> EpisodeGroup: + """ Gets the :class:`~tmdbapis.objs.reload.EpisodeGroup` for the given id. + + Parameters: + episode_group_id (str): Episode Group ID that you want. + load (bool): Load the data on creation. + + Returns: + :class:`~tmdbapis.objs.reload.EpisodeGroup` + + Raises: + :class:`~tmdbapis.exceptions.NotFound`: When no keyword is found for the given id. + """ + return EpisodeGroup(self, {"id": episode_group_id}, load=load) + + def provider_regions(self, reload: bool = False) -> List[Country]: + """ Gets a List of :class:`~tmdbapis.objs.simple.Country`. + + Parameters: + reload (bool): Reload the cached data. + + Returns: + :class:`~tmdbapis.objs.reload.List[Country]` + """ + if reload or self._provider_regions is None: + self._provider_regions = [ + Country(self, c) for c in + self._api.watch_providers_get_available_regions(self.language) + ] + return self._provider_regions + + def movie_providers(self, watch_region: Optional[Union[Country, str]] = None, reload: bool = False) -> List[WatchProvider]: + """ Gets a List of :class:`~tmdbapis.objs.simple.WatchProvider` for movies. + + Parameters: + watch_region (Optional[Union[Country, str]]): Specify a ISO 3166-1 code or :class:`tmdbapis.objs.simple.Country` Object to filter release dates. Must be uppercase. + reload (bool): Reload the cached data. + + Returns: + :class:`~tmdbapis.objs.reload.List[WatchProvider]` + """ + if reload or self._movie_providers is None: + self._movie_providers = [ + WatchProvider(self, w) for w in + self._api.watch_providers_get_movie_providers(self.language, watch_region=watch_region)["results"] + ] + return self._movie_providers + + def tv_providers(self, watch_region: Optional[Union[Country, str]] = None, reload: bool = False) -> List[WatchProvider]: + """ Gets a List of :class:`~tmdbapis.objs.simple.WatchProvider` for shows. + + Parameters: + watch_region (Optional[Union[Country, str]]): Specify a ISO 3166-1 code or :class:`tmdbapis.objs.simple.Country` Object to filter release dates. Must be uppercase. + reload (bool): Reload the cached data. + + Returns: + :class:`~tmdbapis.objs.reload.List[Country]` + """ + if reload or self._tv_providers is None: + self._tv_providers = [ + WatchProvider(self, w) for w in + self._api.watch_providers_get_tv_providers(self.language, watch_region=watch_region)["results"] + ] + return self._tv_providers diff --git a/tmdbapis/util.py b/tmdbapis/util.py new file mode 100644 index 0000000..a538b24 --- /dev/null +++ b/tmdbapis/util.py @@ -0,0 +1,146 @@ +from datetime import datetime + +from .objs.simple import Language, Country +from .exceptions import Invalid + +discover_movie_options = [ + "region", "sort_by", "certification_country", "certification", "certification.lte", + "certification.gte", "include_adult", "include_video", "primary_release_year", + "primary_release_date.gte", "primary_release_date.lte", "release_date.gte", "release_date.lte", + "with_release_type", "year", "vote_count.gte", "vote_count.lte", "vote_average.gte", "vote_average.lte", + "with_cast", "with_crew", "with_people", "with_companies", "with_genres", "without_genres", + "with_keywords", "without_keywords", "with_runtime.gte", "with_runtime.lte", "with_original_language", + "with_watch_providers", "watch_region", "with_watch_monetization_types" +] + +discover_movie_sort_options = [ + "popularity.asc", "popularity.desc", "release_date.asc", "release_date.desc", "revenue.asc", "revenue.desc", + "primary_release_date.asc", "primary_release_date.desc", "original_title.asc", "original_title.desc", + "vote_average.asc", "vote_average.desc", "vote_count.asc", "vote_count.desc" +] + +discover_tv_options = [ + "sort_by", "air_date.gte", "air_date.lte", "first_air_date.gte", "first_air_date.lte", "first_air_date_year", + "timezone", "vote_average.gte", "vote_average.lte", "vote_count.gte", "vote_count.lte", "with_genres", + "with_networks", "without_genres", "with_runtime.gte", "with_runtime.lte", "include_null_first_air_dates", + "with_original_language", "without_keywords", "screened_theatrically", "with_companies", "with_keywords", + "with_watch_providers", "watch_region", "with_watch_monetization_types" +] + +discover_tv_sort_options = [ + "popularity.asc", "popularity.desc", "first_air_date.desc", + "first_air_date.asc", "vote_average.asc", "vote_average.desc" +] + +v3_sorts = ["created_at.asc", "created_at.desc"] +v4_movie_sorts = ["created_at.asc", "created_at.desc", "release_date.asc", "release_date.desc", + "title.asc", "title.desc", "vote_average.asc", "vote_average.desc"] +v4_show_sorts = ["created_at.asc", "created_at.desc", "first_air_date.asc", "first_air_date.desc", + "name.asc", "name.desc", "vote_average.asc", "vote_average.desc"] +v4_list_sorts = ["original_order.asc", "original_order.desc", "vote_average.asc", "vote_average.desc", + "primary_release_date.asc", "primary_release_date.desc", "title.asc", "title.desc"] + +def validate_sort(sort_by, v3, is_movie): + if not sort_by: + return None + elif v3 and sort_by not in v3_sorts: + raise Invalid(f"sort_by not in {v3_sorts}") + elif not v3 and is_movie and sort_by not in v4_movie_sorts: + raise Invalid(f"sort_by not in {v4_movie_sorts}") + elif not v3 and not is_movie and sort_by not in v4_show_sorts: + raise Invalid(f"sort_by not in {v4_show_sorts}") + else: + return sort_by + +def validate_items(items, comment=False): + json = {"items": []} + for item in items: + if "media_type" not in item or "media_id" not in item or item["media_type"] not in ["movie", "tv"] or \ + (comment and "comment" not in item): + error = "'media_type', 'media_id', and 'comment'" if comment else "'media_type' and 'media_id'" + raise Invalid(f"Each object must have {error}. 'media_type' must be either 'movie' or 'tv'") + json["items"].append({"media_type": item["media_type"], "media_id": int(item["media_id"])}) + return json + +def validate_language(language, languages): + if isinstance(language, Language): + return language.iso_639_1 + elif str(language).lower() in languages: + return str(language).lower() + else: + raise Invalid(f"Language: {language} is invalid see Configuration.languages for the options.") + +def validate_country(country, countries): + if not country: + return None + elif isinstance(country, Country): + return country.iso_3166_1 + elif str(country).lower() in countries: + return str(country).lower() + else: + raise Invalid(f"Country: {country} is invalid see Configuration.countries for the options.") + +def validate_discover(is_movie, **kwargs): + validated = {} + for k, v in kwargs.items(): + if is_movie and k not in discover_movie_options or not is_movie and k not in discover_tv_options: + raise Invalid(f"{k} is not a valid parameter") + elif k == "sort_by": + if is_movie and k not in discover_movie_sort_options or not is_movie and k not in discover_tv_sort_options: + raise Invalid(f"{v} is not a valid sort_by option") + validated[k] = v + elif k == "region": + if len(v) != 2: + raise Invalid(f"{v} is not a region option it can only be two characters") + validated[k] = str(v).upper() + elif k == "certification_country": + if "certification" not in kwargs and "certification.lte" not in kwargs and "certification.gte" not in kwargs: + raise Invalid("certification_country must be used with either certification, certification.lte, or certification.gte") + validated[k] = str(v) + elif k in ["certification", "certification.lte", "certification.gte"]: + if "certification_country" not in kwargs: + raise Invalid("certification must be used with certification_country") + validated[k] = str(v) + elif k in ["include_adult", "include_video", "include_null_first_air_dates", "screened_theatrically"]: + if not isinstance(v, bool): + raise Invalid(f"{k} must be either True or False") + validated[k] = v + elif k in ["primary_release_date.gte", "primary_release_date.lte", "release_date.gte", "release_date.lte", + "air_date.gte", "air_date.lte", "first_air_date.gte", "first_air_date.lte"]: + if isinstance(v, datetime): + date_obj = v + else: + try: + date_obj = datetime.strptime(str(v), "%Y-%m-%d") + except ValueError: + raise Invalid(f"{k} must be a datetime object or match pattern YYYY-MM-DD (e.g. 2020-12-25)") + validated[k] = datetime.strftime(date_obj, "%Y-%m-%d") + elif k in ["primary_release_year", "first_air_date_year", "vote_count.gte", "vote_count.lte", + "with_runtime.gte", "with_runtime.lte"]: + if not isinstance(v, int) or v < 1: + raise Invalid(f"{k} must be an integer greater then 0") + validated[k] = v + elif k in ["vote_average.gte", "vote_average.lte"]: + if not isinstance(v, float) or v < 1: + raise Invalid(f"{k} must be a number greater then 0.0") + validated[k] = v + elif k == "with_watch_monetization_types": + if "watch_region" not in kwargs: + raise Invalid("with_watch_monetization_types must be used with watch_region") + if v not in ["flatrate", "free", "ads", "rent", "buy"]: + raise Invalid(f"{v} is not a valid with_watch_monetization_types option. Options: [flatrate, free, ads, rent, or buy]") + validated[k] = v + else: + validated[k] = ",".join([str(x) for x in v]) if isinstance(v, list) else str(v) + return validated + +def validate_date(data, date_format="%Y-%m-%d"): + if not data: + return None + elif isinstance(data, datetime): + return data.strftime(date_format) + else: + try: + return datetime.strptime(str(data), date_format).strftime(date_format) + except ValueError: + raise Invalid(f"date: {data} must be a datetime or in the format YYYY-MM-DD")