diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..7c850b6
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,28 @@
+# Windows image file caches
+Thumbs.db
+
+# Folder config file
+Desktop.ini
+
+# Mac crap
+.DS_Store
+
+# eclipse crap
+.settings
+.settings/org.eclipse.core.resources.prefs
+
+# Eclipse Files
+#.pydevproject
+#.project
+#.settings
+
+# Python Files
+*.pyo
+*.pyc
+
+# Ignore *.mo Files in po-folder
+/po/*.mo
+
+# For lunatic users of multiple revision control systems
+.svn
+.svnignore
diff --git a/.pylintrc b/.pylintrc
new file mode 100644
index 0000000..db4a6ec
--- /dev/null
+++ b/.pylintrc
@@ -0,0 +1,548 @@
+[MASTER]
+
+# A comma-separated list of package or module names from where C extensions may
+# be loaded. Extensions are loading into the active Python interpreter and may
+# run arbitrary code
+extension-pkg-whitelist=
+
+# Add files or directories to the blacklist. They should be base names, not
+# paths.
+ignore=CVS
+
+# Add files or directories matching the regex patterns to the blacklist. The
+# regex matches against base names, not paths.
+ignore-patterns=
+
+# Python code to execute, usually for sys.path manipulation such as
+# pygtk.require().
+#init-hook=
+
+# Use multiple processes to speed up Pylint.
+jobs=1
+
+# List of plugins (as comma separated values of python modules names) to load,
+# usually to register additional checkers.
+load-plugins=
+
+# Pickle collected data for later comparisons.
+persistent=yes
+
+# Specify a configuration file.
+#rcfile=
+
+# When enabled, pylint would attempt to guess common misconfiguration and emit
+# user-friendly hints instead of false-positive error messages
+suggestion-mode=yes
+
+# Allow loading of arbitrary C extensions. Extensions are imported into the
+# active Python interpreter and may run arbitrary code.
+unsafe-load-any-extension=no
+
+
+[MESSAGES CONTROL]
+
+# Only show warnings with the listed confidence levels. Leave empty to show
+# all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED
+confidence=
+
+# Disable the message, report, category or checker with the given id(s). You
+# can either give multiple identifiers separated by comma (,) or put this
+# option multiple times (only on the command line, not in the configuration
+# file where it should appear only once).You can also use "--disable=all" to
+# disable everything first and then reenable specific checks. For example, if
+# you want to run only the similarities checker, you can use "--disable=all
+# --enable=similarities". If you want to run only the classes checker, but have
+# no Warning level messages displayed, use"--disable=all --enable=classes
+# --disable=W"
+disable=print-statement,
+ parameter-unpacking,
+ unpacking-in-except,
+ old-raise-syntax,
+ backtick,
+ long-suffix,
+ old-ne-operator,
+ old-octal-literal,
+ import-star-module-level,
+ non-ascii-bytes-literal,
+ raw-checker-failed,
+ bad-inline-option,
+ locally-disabled,
+ locally-enabled,
+ file-ignored,
+ suppressed-message,
+ useless-suppression,
+ deprecated-pragma,
+ apply-builtin,
+ basestring-builtin,
+ buffer-builtin,
+ cmp-builtin,
+ coerce-builtin,
+ execfile-builtin,
+ file-builtin,
+ long-builtin,
+ raw_input-builtin,
+ reduce-builtin,
+ standarderror-builtin,
+ unicode-builtin,
+ xrange-builtin,
+ coerce-method,
+ delslice-method,
+ getslice-method,
+ setslice-method,
+ no-absolute-import,
+ old-division,
+ dict-iter-method,
+ dict-view-method,
+ next-method-called,
+ metaclass-assignment,
+ indexing-exception,
+ raising-string,
+ reload-builtin,
+ oct-method,
+ hex-method,
+ nonzero-method,
+ cmp-method,
+ input-builtin,
+ round-builtin,
+ intern-builtin,
+ unichr-builtin,
+ map-builtin-not-iterating,
+ zip-builtin-not-iterating,
+ range-builtin-not-iterating,
+ filter-builtin-not-iterating,
+ using-cmp-argument,
+ eq-without-hash,
+ div-method,
+ idiv-method,
+ rdiv-method,
+ exception-message-attribute,
+ invalid-str-codec,
+ sys-max-int,
+ bad-python3-import,
+ deprecated-string-function,
+ deprecated-str-translate-call,
+ deprecated-itertools-function,
+ deprecated-types-field,
+ next-method-defined,
+ dict-items-not-iterating,
+ dict-keys-not-iterating,
+ dict-values-not-iterating,
+
+ R0201, R0205, R1725,
+ C0103, C0114, C0326, C0330, C0411,
+ W0122, W0123, W0201, W0311, W0312, W0603, W0703, W1505,
+ E0401, E0611, E1101
+
+# Enable the message, report, category or checker with the given id(s). You can
+# either give multiple identifier separated by comma (,) or put this option
+# multiple time (only on the command line, not in the configuration file where
+# it should appear only once). See also the "--disable" option for examples.
+enable=c-extension-no-member
+
+
+[REPORTS]
+
+# Python expression which should return a note less than 10 (10 is the highest
+# note). You have access to the variables errors warning, statement which
+# respectively contain the number of errors / warnings messages and the total
+# number of statements analyzed. This is used by the global evaluation report
+# (RP0004).
+evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10)
+
+# Template used to display messages. This is a python new-style format string
+# used to format the message information. See doc for all details
+msg-template='{msg_id}:{line:3d},{column}: {obj}: {msg}'
+
+# Set the output format. Available formats are text, parseable, colorized, json
+# and msvs (visual studio).You can also give a reporter class, eg
+# mypackage.mymodule.MyReporterClass.
+output-format=text
+
+# Tells whether to display a full report or only the messages
+reports=no
+
+# Activate the evaluation score.
+score=yes
+
+
+[REFACTORING]
+
+# Maximum number of nested blocks for function / method body
+max-nested-blocks=10
+
+# Complete name of functions that never returns. When checking for
+# inconsistent-return-statements if a never returning function is called then
+# it will be considered as an explicit return statement and no message will be
+# printed.
+never-returning-functions=optparse.Values,sys.exit
+
+
+[SPELLING]
+
+# Limits count of emitted suggestions for spelling mistakes
+max-spelling-suggestions=4
+
+# Spelling dictionary name. Available dictionaries: none. To make it working
+# install python-enchant package.
+spelling-dict=
+
+# List of comma separated words that should not be checked.
+spelling-ignore-words=
+
+# A path to a file that contains private dictionary; one word per line.
+spelling-private-dict-file=
+
+# Tells whether to store unknown words to indicated private dictionary in
+# --spelling-private-dict-file option instead of raising a message.
+spelling-store-unknown-words=no
+
+
+[SIMILARITIES]
+
+# Ignore comments when computing similarities.
+ignore-comments=yes
+
+# Ignore docstrings when computing similarities.
+ignore-docstrings=yes
+
+# Ignore imports when computing similarities.
+ignore-imports=no
+
+# Minimum lines number of a similarity.
+min-similarity-lines=50
+
+
+[MISCELLANEOUS]
+
+# List of note tags to take in consideration, separated by a comma.
+notes=FIXME,
+ XXX,
+ TODO
+
+
+[FORMAT]
+
+# Expected format of line ending, e.g. empty (any line ending), LF or CRLF.
+expected-line-ending-format=
+
+# Regexp for a line that is allowed to be longer than the limit.
+ignore-long-lines=^\s*(# )??$
+
+# Number of spaces of indent required inside a hanging or continued line.
+indent-after-paren=4
+
+# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1
+# tab).
+indent-string=' '
+
+# Maximum number of characters on a single line.
+max-line-length=300
+
+# Maximum number of lines in a module
+max-module-lines=1000
+
+# List of optional constructs for which whitespace checking is disabled. `dict-
+# separator` is used to allow tabulation in dicts, etc.: {1 : 1,\n222: 2}.
+# `trailing-comma` allows a space between comma and closing bracket: (a, ).
+# `empty-line` allows space-only lines.
+no-space-check=trailing-comma,
+ dict-separator
+
+# Allow the body of a class to be on the same line as the declaration if body
+# contains single statement.
+single-line-class-stmt=no
+
+# Allow the body of an if to be on the same line as the test if there is no
+# else.
+single-line-if-stmt=no
+
+
+[LOGGING]
+
+# Logging modules to check that the string format arguments are in logging
+# function parameter format
+logging-modules=logging
+
+
+[VARIABLES]
+
+# List of additional names supposed to be defined in builtins. Remember that
+# you should avoid to define new builtins when possible.
+additional-builtins=
+
+# Tells whether unused global variables should be treated as a violation.
+allow-global-unused-variables=yes
+
+# List of strings which can identify a callback function by name. A callback
+# name must start or end with one of those strings.
+callbacks=cb_,
+ _cb
+
+# A regular expression matching the name of dummy variables (i.e. expectedly
+# not used).
+#dummy-variables-rgx=_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy|^ignored_|^unused_
+dummy-variables-rgx=(_+[a-zA-Z0-9_]*?$)|dummy
+
+# Argument names that match this expression will be ignored. Default to name
+# with leading underscore
+ignored-argument-names=_.*|^ignored_|^unused_
+
+# Tells whether we should check for unused import in __init__ files.
+init-import=no
+
+# List of qualified module names which can have objects that can redefine
+# builtins.
+redefining-builtins-modules=six.moves,past.builtins,future.builtins
+
+
+[TYPECHECK]
+
+# List of decorators that produce context managers, such as
+# contextlib.contextmanager. Add to this list to register other decorators that
+# produce valid context managers.
+contextmanager-decorators=contextlib.contextmanager
+
+# List of members which are set dynamically and missed by pylint inference
+# system, and so shouldn't trigger E1101 when accessed. Python regular
+# expressions are accepted.
+generated-members=
+
+# Tells whether missing members accessed in mixin class should be ignored. A
+# mixin class is detected if its name ends with "mixin" (case insensitive).
+ignore-mixin-members=yes
+
+# This flag controls whether pylint should warn about no-member and similar
+# checks whenever an opaque object is returned when inferring. The inference
+# can return multiple potential results while evaluating a Python object, but
+# some branches might not be evaluated, which results in partial inference. In
+# that case, it might be useful to still emit no-member and other checks for
+# the rest of the inferred objects.
+ignore-on-opaque-inference=yes
+
+# List of class names for which member attributes should not be checked (useful
+# for classes with dynamically set attributes). This supports the use of
+# qualified names.
+ignored-classes=optparse.Values,thread._local,_thread._local
+
+# List of module names for which member attributes should not be checked
+# (useful for modules/projects where namespaces are manipulated during runtime
+# and thus existing member attributes cannot be deduced by static analysis. It
+# supports qualified module names, as well as Unix pattern matching.
+ignored-modules=
+
+# Show a hint with possible names when a member name was not found. The aspect
+# of finding the hint is based on edit distance.
+missing-member-hint=yes
+
+# The minimum edit distance a name should have in order to be considered a
+# similar match for a missing member name.
+missing-member-hint-distance=1
+
+# The total number of similar names that should be taken in consideration when
+# showing a hint for a missing member.
+missing-member-max-choices=1
+
+
+[BASIC]
+
+# Naming style matching correct argument names
+argument-naming-style=snake_case
+
+# Regular expression matching correct argument names. Overrides argument-
+# naming-style
+#argument-rgx=
+
+# Naming style matching correct attribute names
+attr-naming-style=snake_case
+
+# Regular expression matching correct attribute names. Overrides attr-naming-
+# style
+#attr-rgx=
+
+# Bad variable names which should always be refused, separated by a comma
+bad-names=foo,
+ bar,
+ baz,
+ toto,
+ tutu,
+ tata
+
+# Naming style matching correct class attribute names
+class-attribute-naming-style=any
+
+# Regular expression matching correct class attribute names. Overrides class-
+# attribute-naming-style
+#class-attribute-rgx=
+
+# Naming style matching correct class names
+class-naming-style=PascalCase
+
+# Regular expression matching correct class names. Overrides class-naming-style
+#class-rgx=
+
+# Naming style matching correct constant names
+const-naming-style=UPPER_CASE
+
+# Regular expression matching correct constant names. Overrides const-naming-
+# style
+#const-rgx=
+
+# Minimum line length for functions/classes that require docstrings, shorter
+# ones are exempt.
+docstring-min-length=1000
+
+# Naming style matching correct function names
+function-naming-style=camelCase
+
+# Regular expression matching correct function names. Overrides function-
+# naming-style
+#function-rgx=
+
+# Good variable names which should always be accepted, separated by a comma
+good-names=i,
+ j,
+ k,
+ ex,
+ Run,
+ _
+
+# Include a hint for the correct naming format with invalid-name
+include-naming-hint=no
+
+# Naming style matching correct inline iteration names
+inlinevar-naming-style=any
+
+# Regular expression matching correct inline iteration names. Overrides
+# inlinevar-naming-style
+#inlinevar-rgx=
+
+# Naming style matching correct method names
+method-naming-style=camelCase
+
+# Regular expression matching correct method names. Overrides method-naming-
+# style
+#method-rgx=
+
+# Naming style matching correct module names
+module-naming-style=any
+
+# Regular expression matching correct module names. Overrides module-naming-
+# style
+#module-rgx=
+
+# Colon-delimited sets of names that determine each other's naming style when
+# the name regexes allow several styles.
+name-group=
+
+# Regular expression which should only match function or class names that do
+# not require a docstring.
+no-docstring-rgx=^_
+
+# List of decorators that produce properties, such as abc.abstractproperty. Add
+# to this list to register other decorators that produce valid properties.
+property-classes=abc.abstractproperty
+
+# Naming style matching correct variable names
+variable-naming-style=snake_case
+
+# Regular expression matching correct variable names. Overrides variable-
+# naming-style
+#variable-rgx=
+
+
+[IMPORTS]
+
+# Allow wildcard imports from modules that define __all__.
+allow-wildcard-with-all=no
+
+# Analyse import fallback blocks. This can be used to support both Python 2 and
+# 3 compatible code, which means that the block might have code that exists
+# only in one or another interpreter, leading to false positives when analysed.
+analyse-fallback-blocks=no
+
+# Deprecated modules which should not be used, separated by a comma
+deprecated-modules=regsub,
+ TERMIOS,
+ Bastion,
+ rexec
+
+# Create a graph of external dependencies in the given file (report RP0402 must
+# not be disabled)
+ext-import-graph=
+
+# Create a graph of every (i.e. internal and external) dependencies in the
+# given file (report RP0402 must not be disabled)
+import-graph=
+
+# Create a graph of internal dependencies in the given file (report RP0402 must
+# not be disabled)
+int-import-graph=
+
+# Force import order to recognize a module as part of the standard
+# compatibility libraries.
+known-standard-library=
+
+# Force import order to recognize a module as part of a third party library.
+known-third-party=enchant
+
+
+[DESIGN]
+
+# Maximum number of arguments for function / method
+max-args=100
+
+# Maximum number of attributes for a class (see R0902).
+max-attributes=100
+
+# Maximum number of boolean expressions in a if statement
+max-bool-expr=100
+
+# Maximum number of branch for function / method body
+max-branches=100
+
+# Maximum number of locals for function / method body
+max-locals=100
+
+# Maximum number of parents for a class (see R0901).
+max-parents=100
+
+# Maximum number of public methods for a class (see R0904).
+max-public-methods=100
+
+# Maximum number of return / yield for function / method body
+max-returns=100
+
+# Maximum number of statements in function / method body
+max-statements=200
+
+# Minimum number of public methods for a class (see R0903).
+min-public-methods=0
+
+
+[CLASSES]
+
+# List of method names used to declare (i.e. assign) instance attributes.
+defining-attr-methods=__init__,
+ __new__,
+ setUp
+
+# List of member names, which should be excluded from the protected access
+# warning.
+exclude-protected=_asdict,
+ _fields,
+ _replace,
+ _source,
+ _make
+
+# List of valid names for the first argument in a class method.
+valid-classmethod-first-arg=cls
+
+# List of valid names for the first argument in a metaclass class method.
+valid-metaclass-classmethod-first-arg=mcs
+
+
+[EXCEPTIONS]
+
+# Exceptions that will emit a warning when being caught. Defaults to
+# "Exception"
+overgeneral-exceptions=Exception
diff --git a/CONTROL/control b/CONTROL/control
new file mode 100644
index 0000000..176192e
--- /dev/null
+++ b/CONTROL/control
@@ -0,0 +1,8 @@
+Description: TMDBCockpit
+Maintainer: dream-alpha
+Package: enigma2-plugin-extensions-tmdbcockpit
+Version: 8.9.4
+Architecture: all
+Depends: python-requests
+Conflicts: enigma2-plugin-extensions-tmdb
+Replaces: enigma2-plugin-extensions-tmdb
diff --git a/CONTROL/postinst b/CONTROL/postinst
new file mode 100755
index 0000000..2882138
--- /dev/null
+++ b/CONTROL/postinst
@@ -0,0 +1,4 @@
+#!/bin/sh
+echo "Plugin successfully installed."
+echo "Please restart DreamOS now!"
+exit 0
diff --git a/CONTROL/postrm b/CONTROL/postrm
new file mode 100755
index 0000000..f615d1a
--- /dev/null
+++ b/CONTROL/postrm
@@ -0,0 +1,8 @@
+#!/bin/sh
+if [ "$1" = "remove" ]; then
+ rm -rf /usr/lib/enigma2/python/Plugins/Extensions/TMDBCockpit > /dev/null 2>&1
+ echo "TMDBCockpit plugin removed successfully."
+else
+ find /usr/lib/enigma2/python/Plugins/Extensions/TMDBCockpit -type f -name "*.pyo" -exec rm -f {} \; > /dev/null 2>&1
+fi
+exit 0
diff --git a/CONTROL/preinst b/CONTROL/preinst
new file mode 100755
index 0000000..039e4d0
--- /dev/null
+++ b/CONTROL/preinst
@@ -0,0 +1,2 @@
+#!/bin/sh
+exit 0
diff --git a/Makefile.am b/Makefile.am
new file mode 100644
index 0000000..cc366b1
--- /dev/null
+++ b/Makefile.am
@@ -0,0 +1 @@
+SUBDIRS = po src
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..2db89eb
--- /dev/null
+++ b/README.md
@@ -0,0 +1,28 @@
+[![Codacy Badge](https://app.codacy.com/project/badge/Grade/495cf6fc5be8434ca7b493ff88724433)](https://www.codacy.com/gh/dream-alpha/TMDBCockpit/dashboard?utm_source=github.com&utm_medium=referral&utm_content=dream-alpha/TMDBCockpit&utm_campaign=Badge_Grade)
+[![Gemfury](https://badge.fury.io/fp/gemfury.svg)](https://gemfury.com/f/partner)
+
+# The Movie Database plugin TMDBCockpit
+![Screenshot](TMDBCockpit.png)
+## Features
+- Shows detailed movie/tv show information provided by TMDB.
+- Can be invoked thru movie lists (standard, EMC, etc.) or thru yellow or blue key in the EPG lists.
+- Provides an interface for other plugins to access TMDB data without opening TMDBCockpit screens.
+- Allows to save movie as well as series episode covers, descriptions and backdrops.
+- Allows unique tmdb api key in /etc/enigma2/tmdb_key.txt
+- Allows playback of trailer with either MediaPortal or MyTube
+- Supports HD, FHD, and WQHD skin resolutions.
+
+## Languages
+- english
+- german
+- arabic (by ostende)
+- italian (by Spaeleus)
+- russian (by ay4488)
+- turkish (by audi06_19)
+- spanish (by Magog)
+- dutch (by msatter)
+
+## Links
+- Installation: https://dream-alpha.github.io/TMDBCockpit
+- Support: https://github.com/dream-alpha/TMDBCockpit/discussions
+- Package feed: https://gemfury.com/dream-alpha
diff --git a/TMDBCockpit.png b/TMDBCockpit.png
new file mode 100755
index 0000000..f82ffdf
Binary files /dev/null and b/TMDBCockpit.png differ
diff --git a/docs/index.html b/docs/index.html
new file mode 100644
index 0000000..812509e
--- /dev/null
+++ b/docs/index.html
@@ -0,0 +1,15 @@
+
+
+ TMDBCockpit Installation
+ To install the TMDBCockpit plugin execute the following commands in a telnet console on your dreambox:
+
+ - apt-get install wget (required the first time only)
+
- wget https://dream-alpha.github.io/TMDBCockpit/tmdbcockpit.sh -O - | /bin/sh
+
+ The installation script will also install a feed source that enables a convenient upgrade to the latest version with the following commands or automatically as part of a DreamOS upgrade:
+
+ - apt-get update
+
- apt-get upgrade
+
+
+
diff --git a/docs/tmdbcockpit.sh b/docs/tmdbcockpit.sh
new file mode 100755
index 0000000..7c2de12
--- /dev/null
+++ b/docs/tmdbcockpit.sh
@@ -0,0 +1,8 @@
+#!/bin/sh
+rm -f /etc/apt/sources.list.d/cockpit.list
+apt-get update
+apt-get install -y apt-transport-https
+echo deb [trusted=yes] https://apt.fury.io/dream-alpha ./ > /etc/apt/sources.list.d/cockpit.list
+apt-get update
+apt-get install -y enigma2-plugin-extensions-tmdbcockpit
+systemctl restart enigma2
diff --git a/po/Makefile.am b/po/Makefile.am
new file mode 100644
index 0000000..c03e7b1
--- /dev/null
+++ b/po/Makefile.am
@@ -0,0 +1,61 @@
+#
+# to use this for the localisation of other plugins,
+# just change the DOMAIN to the name of the Plugin.
+# It is assumed, that the domain ist the same as
+# the directory name of the plugin.
+#
+
+DOMAIN = TMDBCockpit
+installdir = $(libdir)/enigma2/python/Plugins/Extensions/$(DOMAIN)
+#GETTEXT=./pygettext.py
+GETTEXT=xgettext
+
+#MSGFMT = ./msgfmt.py
+MSGFMT = msgfmt
+
+LANGS := ar de it ru tr es nl
+LANGPO := $(foreach LANG, $(LANGS),$(LANG).po)
+LANGMO := $(foreach LANG, $(LANGS),$(LANG).mo)
+
+default: $(DOMAIN).pot $(LANGPO) merge $(LANGMO)
+ for lang in $(LANGS); do \
+ mkdir -p $$lang/LC_MESSAGES; \
+ cp $$lang.mo $$lang/LC_MESSAGES/$(DOMAIN).mo; \
+ cp $$lang.po $$lang/LC_MESSAGES/$$lang.po; \
+ done
+
+merge:
+ for lang in $(LANGS); do \
+ msgmerge --no-location -s -N -U $$lang.po $(DOMAIN).pot; \
+ done
+
+
+# the TRANSLATORS: allows putting translation comments before the to-be-translated line.
+$(DOMAIN).pot:
+ $(GETTEXT) -L python --add-comments="TRANSLATORS:" -d $(DOMAIN) -s -o $(DOMAIN).pot ../*.py
+
+ ../xml2po.py ../ >> $(DOMAIN).pot
+
+ msguniq -o $(DOMAIN)uniq.pot $(DOMAIN).pot
+
+
+.PHONY: $(DOMAIN).pot
+
+
+%.mo: %.po
+ $(MSGFMT) -o $@ $<
+
+%.po:
+ msginit -l $@ -o $@ -i $(DOMAIN).pot --no-translator
+
+CLEANFILES = $(foreach LANG, $(LANGS),$(LANG).mo)
+
+clean-local:
+ $(RM) -r $(LANGS)
+
+install-data-am: default
+ for lang in $(LANGS); do \
+ mkdir -p $(DESTDIR)$(installdir)/locale/$$lang/LC_MESSAGES; \
+ cp $$lang.mo $(DESTDIR)$(installdir)/locale/$$lang/LC_MESSAGES/$(DOMAIN).mo; \
+ cp $$lang.po $(DESTDIR)$(installdir)/locale/$$lang/LC_MESSAGES/$$lang.po; \
+ done
diff --git a/po/TMDBCockpit.pot b/po/TMDBCockpit.pot
new file mode 100644
index 0000000..aba2dc7
--- /dev/null
+++ b/po/TMDBCockpit.pot
@@ -0,0 +1,257 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: TMDBCockpit\n"
+"POT-Creation-Date: 2024-07-09 18:36+0200\n"
+"PO-Revision-Date: 2024-07-09 18:37+0200\n"
+"Last-Translator: dream-alpha\n"
+"Language-Team: \n"
+"Language: de\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"X-Generator: Poedit 3.4.4\n"
+"X-Poedit-KeywordsList: _;gettext;gettext_noop\n"
+"X-Poedit-Basepath: ../src\n"
+"X-Poedit-SourceCharset: UTF-8\n"
+"X-Poedit-SearchPath-0: .\n"
+
+msgid "TMDB Infos"
+msgstr ""
+
+msgid "Save movie description"
+msgstr ""
+
+msgid "Save movie cover"
+msgstr ""
+
+msgid "Save movie backdrop"
+msgstr ""
+
+msgid "Save movie backdrop as cover"
+msgstr ""
+
+msgid "TMDB cover/backdrop"
+msgstr ""
+
+msgid "Please select a function"
+msgstr ""
+
+msgid "Cover saved."
+msgstr ""
+
+msgid "No cover available"
+msgstr ""
+
+msgid "Backdrop saved."
+msgstr ""
+
+msgid "No backdrop available"
+msgstr ""
+
+msgid "Movie description saved."
+msgstr ""
+
+msgid "No movie description available"
+msgstr ""
+
+msgid "Various"
+msgstr ""
+
+msgid "Seasons"
+msgstr ""
+
+msgid "Episodes"
+msgstr ""
+
+msgid "Season"
+msgstr ""
+
+msgid "female"
+msgstr ""
+
+msgid "male"
+msgstr ""
+
+msgid "divers"
+msgstr ""
+
+msgid "not specified"
+msgstr ""
+
+msgid "OK"
+msgstr ""
+
+msgid "Cancel"
+msgstr ""
+
+msgid "Language:"
+msgstr ""
+
+msgid "Skip to movie details for single result:"
+msgstr ""
+
+msgid "Yellow key for TMDB infos in EPGs:"
+msgstr ""
+
+msgid "Cover resolution:"
+msgstr ""
+
+msgid "Backdrop resolution:"
+msgstr ""
+
+msgid "Player for trailers:"
+msgstr ""
+
+msgid "Overview"
+msgstr ""
+
+msgid "Details"
+msgstr ""
+
+msgid "Edit search"
+msgstr ""
+
+msgid "more ..."
+msgstr ""
+
+msgid "Show details"
+msgstr ""
+
+msgid "Exit"
+msgstr ""
+
+msgid "Up"
+msgstr ""
+
+msgid "Down"
+msgstr ""
+
+msgid "Details down"
+msgstr ""
+
+msgid "Details up"
+msgstr ""
+
+msgid "Setup"
+msgstr ""
+
+msgid "Current movies in cinemas"
+msgstr ""
+
+msgid "No search string specified."
+msgstr ""
+
+msgid "Looking up: %s ..."
+msgstr ""
+
+msgid "page"
+msgstr ""
+
+msgid "No results for: %s"
+msgstr ""
+
+msgid "Upcoming movies"
+msgstr ""
+
+msgid "Popular movies"
+msgstr ""
+
+msgid "Similar movies"
+msgstr ""
+
+msgid "Recommendations"
+msgstr ""
+
+msgid "Best rated movies"
+msgstr ""
+
+msgid "TMDB categories"
+msgstr ""
+
+msgid "Please select a category"
+msgstr ""
+
+msgid "Search for Movie:"
+msgstr ""
+
+msgid "Movie Details"
+msgstr ""
+
+msgid "Genre:"
+msgstr ""
+
+msgid "Votes:"
+msgstr ""
+
+msgid "Runtime:"
+msgstr ""
+
+msgid "Year:"
+msgstr ""
+
+msgid "Countries:"
+msgstr ""
+
+msgid "Director:"
+msgstr ""
+
+msgid "Author:"
+msgstr ""
+
+msgid "Studio:"
+msgstr ""
+
+msgid "Crew"
+msgstr ""
+
+msgid "Selection up"
+msgstr ""
+
+msgid "Selection down"
+msgstr ""
+
+msgid "Page up"
+msgstr ""
+
+msgid "Page down"
+msgstr ""
+
+msgid "Search"
+msgstr ""
+
+msgid "Videos"
+msgstr ""
+
+msgid "No search provider registered."
+msgstr ""
+
+msgid "TMDB videos"
+msgstr ""
+
+msgid "Please select a video"
+msgstr ""
+
+msgid "Person Details"
+msgstr ""
+
+msgid "Popularity"
+msgstr ""
+
+msgid "Known for:"
+msgstr ""
+
+msgid "Movie"
+msgstr ""
+
+msgid "Series"
+msgstr ""
+
+msgid "Person"
+msgstr ""
+
+msgid "min"
+msgstr ""
+
+msgid "TMDBCockpit"
+msgstr ""
diff --git a/po/ar.po b/po/ar.po
new file mode 100644
index 0000000..675ab92
--- /dev/null
+++ b/po/ar.po
@@ -0,0 +1,324 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: TMDBCockpit\n"
+"POT-Creation-Date: 2023-08-27 06:06+0300\n"
+"PO-Revision-Date: 2023-08-27 06:15+0300\n"
+"Last-Translator: ostende \n"
+"Language-Team: \n"
+"Language: ar\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"X-Generator: Poedit 3.2.2\n"
+"X-Poedit-KeywordsList: _;gettext;gettext_noop\n"
+"X-Poedit-Basepath: ../src\n"
+"X-Poedit-SourceCharset: UTF-8\n"
+"X-Poedit-SearchPath-0: .\n"
+
+
+msgid "Plugin"
+msgstr "وصلة إضافية"
+
+msgid "Version"
+msgstr "الإصدار"
+
+msgid "Copyright"
+msgstr "حقوق النشر"
+
+msgid "License"
+msgstr "ترخيص"
+
+msgid "End"
+msgstr "نهاية"
+
+msgid "Stop playback"
+msgstr "إيقاف التشغيل"
+
+msgid "Skip forward"
+msgstr "تخطي للأمام"
+
+msgid "Skip backward"
+msgstr "تخطي للخلف"
+
+msgid "EPG Info"
+msgstr "معلومات EPG"
+
+msgid "Do you want to resume playback at position: %d:%02d:%02d?"
+msgstr "هل ترغب في استئناف التشغيل في الموقع: %d:%02d:%02d؟"
+
+msgid "Cancel"
+msgstr "إلغاء"
+
+msgid "Save"
+msgstr "حفظ"
+
+msgid "Defaults"
+msgstr "الإعدادات الافتراضية"
+
+msgid "Really close without saving settings?"
+msgstr "هل تريد الخروج حقًا بدون حفظ الإعدادات؟"
+
+msgid "Setup"
+msgstr "الإعدادات"
+
+msgid "Loading default settings will overwrite all settings, really load them?"
+msgstr "سيؤدي تحميل الإعدادات الافتراضية إلى استبدال جميع الإعدادات، هل تريد تحميلها حقًا؟"
+
+msgid "Some changes require a GUI restart"
+msgstr "بعض التغييرات تتطلب إعادة تشغيل واجهة المستخدم"
+
+msgid "Restart GUI now?"
+msgstr "هل ترغب في إعادة تشغيل واجهة المستخدم الآن؟"
+
+msgid "Bookmarks"
+msgstr "الإشارات المرجعية"
+
+msgid "Select directory"
+msgstr "اختيار المجلد"
+
+msgid "Path does not exist"
+msgstr "المسار غير موجود"
+
+msgid "Close"
+msgstr "إغلاق"
+
+msgid "Hide"
+msgstr "إخفاء"
+
+msgid "Cancelling, please wait"
+msgstr "إلغاء، الرجاء الانتظار"
+
+msgid "Processing"
+msgstr "جاري المعالجة"
+
+msgid "of"
+msgstr "من"
+
+msgid "Done"
+msgstr "تم"
+
+msgid "Cancelled"
+msgstr "تم الإلغاء"
+
+msgid "Please wait"
+msgstr "يرجى الانتظار"
+
+msgid "min"
+msgstr "دقيقة"
+
+msgid "TMDB Infos ..."
+msgstr "معلومات TMDB ..."
+
+msgid "Save movie description"
+msgstr "حفظ وصف الفيلم"
+
+msgid "Save movie cover"
+msgstr "حفظ غلاف الفيلم"
+
+msgid "Save movie backdrop"
+msgstr "حفظ خلفية الفيلم"
+
+msgid "Save movie backdrop as cover"
+msgstr "حفظ خلفية الفيلم كغلاف"
+
+msgid "File operation results:"
+msgstr "نتائج عملية الملفات:"
+
+msgid "Cover saved."
+msgstr "تم حفظ الغلاف."
+
+msgid "No cover available"
+msgstr "لا يتوفر غلاف"
+
+msgid "Backdrop saved."
+msgstr "تم حفظ الخلفية."
+
+msgid "No backdrop available"
+msgstr "لا توجد خلفية متوفرة"
+
+msgid "Movie description saved."
+msgstr "تم حفظ وصف الفيلم."
+
+msgid "No movie description available"
+msgstr "لا يتوفر وصف للفيلم"
+
+msgid "Various"
+msgstr "متنوع"
+
+msgid "Seasons"
+msgstr "مواسم"
+
+msgid "Episodes"
+msgstr "حلقات"
+
+msgid "Season"
+msgstr "موسم"
+
+msgid "female"
+msgstr "أنثى"
+
+msgid "male"
+msgstr "ذكر"
+
+msgid "divers"
+msgstr "متنوع"
+
+msgid "None"
+msgstr "لا شيء"
+
+msgid "OK"
+msgstr "موافق"
+
+msgid "Language:"
+msgstr "اللغة:"
+
+msgid "Skip to movie details for single result:"
+msgstr "الانتقال إلى تفاصيل الفيلم للنتيجة الواحدة:"
+
+msgid "Yellow key for TMDB infos in EPGs:"
+msgstr "المفتاح الأصفر لمعلومات TMDB في EPGs:"
+
+msgid "Cover resolution:"
+msgstr "دقة الغلاف:"
+
+msgid "Backdrop resolution:"
+msgstr "دقة الخلفية:"
+
+msgid "Overview"
+msgstr "نظرة عامة"
+
+msgid "Exit"
+msgstr "خروج"
+
+msgid "Details"
+msgstr "تفاصيل"
+
+msgid "Edit search"
+msgstr "تحرير البحث"
+
+msgid "more ..."
+msgstr "المزيد ..."
+
+msgid "Show details"
+msgstr "عرض التفاصيل"
+
+msgid "Details down"
+msgstr "تفاصيل لأسفل"
+
+msgid "Details up"
+msgstr "تفاصيل لأعلى"
+
+msgid "Current movies in cinemas"
+msgstr "الأفلام الحالية في دور العرض"
+
+msgid "No search string specified."
+msgstr "لم يتم تحديد سلسلة بحث."
+
+msgid "Looking up: %s ..."
+msgstr "البحث عن: %s ..."
+
+msgid "page"
+msgstr "صفحة"
+
+msgid "No results for: %s"
+msgstr "لا توجد نتائج لـ: %s"
+
+msgid "Upcoming movies"
+msgstr "الأفلام القادمة"
+
+msgid "Popular movies"
+msgstr "الأفلام الشائعة"
+
+msgid "Similar movies"
+msgstr "الأفلام المماثلة"
+
+msgid "Recommendations"
+msgstr "الأفلام الموصى بها"
+
+msgid "Best rated movies"
+msgstr "أعلى تقييم للأفلام"
+
+msgid "Search for Movie:"
+msgstr "البحث عن فيلم:"
+
+msgid "Movie Details"
+msgstr "تفاصيل الفيلم"
+
+msgid "Genre:"
+msgstr "النوع:"
+
+msgid "Votes:"
+msgstr "الأصوات:"
+
+msgid "Runtime:"
+msgstr "المدة:"
+
+msgid "Year:"
+msgstr "السنة:"
+
+msgid "Countries:"
+msgstr "البلدان:"
+
+msgid "Director:"
+msgstr "المخرج:"
+
+msgid "Author:"
+msgstr "الكاتب:"
+
+msgid "Studio:"
+msgstr "الاستوديو:"
+
+msgid "Crew"
+msgstr "الفريق"
+
+msgid "Selection up"
+msgstr "التحديد لأعلى"
+
+msgid "Selection down"
+msgstr "التحديد لأسفل"
+
+msgid "Page up"
+msgstr "الصفحة لأعلى"
+
+msgid "Page down"
+msgstr "الصفحة لأسفل"
+
+msgid "Search"
+msgstr "بحث"
+
+msgid "Videos"
+msgstr "مقاطع فيديو"
+
+msgid "No search provider registered."
+msgstr "لا توجد مُزودات بحث مُسجلة."
+
+msgid "TMDB videos"
+msgstr "مقاطع فيديو TMDB"
+
+msgid "Please select a video"
+msgstr "الرجاء تحديد فيديو"
+
+msgid "Person Details"
+msgstr "تفاصيل الشخص"
+
+msgid "Popularity"
+msgstr "الشهرة"
+
+msgid "Known for:"
+msgstr "معروف بـ:"
+
+msgid "Movie"
+msgstr "فيلم"
+
+msgid "Series"
+msgstr "مسلسل"
+
+msgid "Person"
+msgstr "شخص"
+
+msgid "TMDBCockpit"
+msgstr "TMDBCockpit"
+
+msgid "Access TMDB movie infos"
+msgstr "الوصول إلى معلومات الأفلام من TMDB"
diff --git a/po/de.po b/po/de.po
new file mode 100644
index 0000000..5c719d0
--- /dev/null
+++ b/po/de.po
@@ -0,0 +1,257 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: TMDBCockpit\n"
+"POT-Creation-Date: 2024-07-09 18:36+0200\n"
+"PO-Revision-Date: 2024-07-09 18:37+0200\n"
+"Last-Translator: dream-alpha\n"
+"Language-Team: \n"
+"Language: de\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"X-Generator: Poedit 3.4.4\n"
+"X-Poedit-KeywordsList: _;gettext;gettext_noop\n"
+"X-Poedit-Basepath: ../src\n"
+"X-Poedit-SourceCharset: UTF-8\n"
+"X-Poedit-SearchPath-0: .\n"
+
+msgid "TMDB Infos"
+msgstr "TMDB Infos "
+
+msgid "Save movie description"
+msgstr "Filmbeschreibung speichern"
+
+msgid "Save movie cover"
+msgstr "Filmcover speichern"
+
+msgid "Save movie backdrop"
+msgstr "Backdrop speichern"
+
+msgid "Save movie backdrop as cover"
+msgstr "Backdrop als Cover speichern"
+
+msgid "TMDB cover/backdrop"
+msgstr "TMDB Cover/Backgrop"
+
+msgid "Please select a function"
+msgstr "Funktionsauswahl"
+
+msgid "Cover saved."
+msgstr "Cover gespeichert."
+
+msgid "No cover available"
+msgstr "Kein Cover verfügbar"
+
+msgid "Backdrop saved."
+msgstr "Backdrop gespeichert."
+
+msgid "No backdrop available"
+msgstr "Kein Backdrop verfügbar"
+
+msgid "Movie description saved."
+msgstr "Filmbeschreibung gespeichert."
+
+msgid "No movie description available"
+msgstr "Keine Filmbeschreibung verfügbar"
+
+msgid "Various"
+msgstr "Verschiedene"
+
+msgid "Seasons"
+msgstr "Staffeln"
+
+msgid "Episodes"
+msgstr "Episoden"
+
+msgid "Season"
+msgstr "Staffel"
+
+msgid "female"
+msgstr "weiblich"
+
+msgid "male"
+msgstr "männlich"
+
+msgid "divers"
+msgstr "divers"
+
+msgid "not specified"
+msgstr "undefiniert"
+
+msgid "OK"
+msgstr "Ok"
+
+msgid "Cancel"
+msgstr "Abbruch"
+
+msgid "Language:"
+msgstr "Sprache:"
+
+msgid "Skip to movie details for single result:"
+msgstr "Springe zur Detailansicht bei nur einem Film:"
+
+msgid "Yellow key for TMDB infos in EPGs:"
+msgstr "Gelbe Taste für TMDB Infos in EPGs:"
+
+msgid "Cover resolution:"
+msgstr "Cover Auflösung:"
+
+msgid "Backdrop resolution:"
+msgstr "Backdrop Auflösung:"
+
+msgid "Player for trailers:"
+msgstr "Player für Trailer:"
+
+msgid "Overview"
+msgstr "Überblick"
+
+msgid "Details"
+msgstr "Details"
+
+msgid "Edit search"
+msgstr "Suche"
+
+msgid "more ..."
+msgstr "mehr ..."
+
+msgid "Show details"
+msgstr "Zeige Details"
+
+msgid "Exit"
+msgstr "Schließen"
+
+msgid "Up"
+msgstr "Hoch"
+
+msgid "Down"
+msgstr "Runter"
+
+msgid "Details down"
+msgstr "Details runter"
+
+msgid "Details up"
+msgstr "Details hoch"
+
+msgid "Setup"
+msgstr "Einstellungen"
+
+msgid "Current movies in cinemas"
+msgstr "Aktuelle Filme im Kino"
+
+msgid "No search string specified."
+msgstr "Suchtext fehlt."
+
+msgid "Looking up: %s ..."
+msgstr "Lade: %s ..."
+
+msgid "page"
+msgstr "Seite"
+
+msgid "No results for: %s"
+msgstr "Keine Ergebnisse für: %s"
+
+msgid "Upcoming movies"
+msgstr "Kommende Filme im Kino"
+
+msgid "Popular movies"
+msgstr "Populäre Filme"
+
+msgid "Similar movies"
+msgstr "Ähnliche Filme"
+
+msgid "Recommendations"
+msgstr "Empfohlene Filme"
+
+msgid "Best rated movies"
+msgstr "Bestbewertete Filme"
+
+msgid "TMDB categories"
+msgstr "TMDB Kategorien"
+
+msgid "Please select a category"
+msgstr "Kategorieauswahl"
+
+msgid "Search for Movie:"
+msgstr "Suche nach Film:"
+
+msgid "Movie Details"
+msgstr "Filmdetails"
+
+msgid "Genre:"
+msgstr "Genre:"
+
+msgid "Votes:"
+msgstr "Stimmen:"
+
+msgid "Runtime:"
+msgstr "Dauer:"
+
+msgid "Year:"
+msgstr "Jahr:"
+
+msgid "Countries:"
+msgstr "Länder:"
+
+msgid "Director:"
+msgstr "Regie:"
+
+msgid "Author:"
+msgstr "Author:"
+
+msgid "Studio:"
+msgstr "Studio:"
+
+msgid "Crew"
+msgstr "Besetzung"
+
+msgid "Selection up"
+msgstr "Auswahl hoch"
+
+msgid "Selection down"
+msgstr "Auswahl runter"
+
+msgid "Page up"
+msgstr "Seite hoch"
+
+msgid "Page down"
+msgstr "Seite runter"
+
+msgid "Search"
+msgstr "Suche"
+
+msgid "Videos"
+msgstr "Trailer"
+
+msgid "No search provider registered."
+msgstr "Keine Suchmaschine registriert"
+
+msgid "TMDB videos"
+msgstr "TMDB Trailer"
+
+msgid "Please select a video"
+msgstr "Trailer Auswahl"
+
+msgid "Person Details"
+msgstr "Personendetails"
+
+msgid "Popularity"
+msgstr "Popularität"
+
+msgid "Known for:"
+msgstr "Bekannt aus:"
+
+msgid "Movie"
+msgstr "Film"
+
+msgid "Series"
+msgstr "Serie"
+
+msgid "Person"
+msgstr "Person"
+
+msgid "min"
+msgstr "Min"
+
+msgid "TMDBCockpit"
+msgstr "TMDBCockpit"
diff --git a/po/es.po b/po/es.po
new file mode 100644
index 0000000..a241f8a
--- /dev/null
+++ b/po/es.po
@@ -0,0 +1,327 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: TMDBCockpit\n"
+"POT-Creation-Date: 2023-05-16 15:57+0200\n"
+"PO-Revision-Date: 2023-05-17 20:41+0200\n"
+"Last-Translator: \n"
+"Language-Team: Magog \n"
+"Language: es_ES\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"X-Generator: Poedit 3.3.1\n"
+"X-Poedit-Basepath: ../../../../../../../../..\n"
+"X-Poedit-SearchPath-0: .\n"
+
+msgid "Plugin"
+msgstr "Plugin"
+
+msgid "Version"
+msgstr "Versión"
+
+msgid "Copyright"
+msgstr "Copyright"
+
+msgid "License"
+msgstr "Licencia"
+
+msgid "End"
+msgstr "Fin"
+
+msgid "Stop playback"
+msgstr "Detener reproducción"
+
+msgid "Skip forward"
+msgstr "Saltar adelante"
+
+msgid "Skip backward"
+msgstr "Saltar atrás"
+
+msgid "EPG Info"
+msgstr "Información EPG"
+
+msgid "Do you want to resume playback at position: %d:%02d:%02d?"
+msgstr "¿Desea reanudar la reproducción en la posición: %d:%02d:%02d?"
+
+msgid "Cancel"
+msgstr "Cancelar"
+
+msgid "Save"
+msgstr "Salvar"
+
+msgid "Defaults"
+msgstr "Por defecto"
+
+msgid "Really close without saving settings?"
+msgstr "¿Cerrar sin guardar la configuración?"
+
+msgid "Setup"
+msgstr "Configurar"
+
+msgid "Loading default settings will overwrite all settings, really load them?"
+msgstr "Cargar la configuración por defecto sobrescribirá todas las configuraciones, ¿proceder?"
+
+msgid "Some changes require a GUI restart"
+msgstr "Algunos cambios requieren un reinicio de la GUI"
+
+msgid "Restart GUI now?"
+msgstr "¿Reiniciar GUI ahora?"
+
+msgid "Bookmarks"
+msgstr "Marcadores"
+
+msgid "Select directory"
+msgstr "Seleccionar directorio"
+
+msgid "Path does not exist"
+msgstr "La ruta no existe"
+
+msgid "Close"
+msgstr "Cerrar"
+
+msgid "Hide"
+msgstr "Ocultar"
+
+msgid "Cancelling, please wait"
+msgstr "Cancelando, por favor espere"
+
+msgid "Processing"
+msgstr "Procesando"
+
+msgid "of"
+msgstr "de"
+
+msgid "Done"
+msgstr "Hecho"
+
+msgid "Cancelled"
+msgstr "Cancelado"
+
+msgid "Please wait"
+msgstr "Espere, por favor"
+
+msgid "min"
+msgstr "min"
+
+msgid "TMDB Infos ..."
+msgstr "Información TMDB..."
+
+msgid "Various"
+msgstr "Varios"
+
+msgid "Seasons"
+msgstr "Temporadas"
+
+msgid "Episodes"
+msgstr "Episodios"
+
+msgid "Season"
+msgstr "Temporada"
+
+msgid "female"
+msgstr "femenino"
+
+msgid "male"
+msgstr "masculino"
+
+msgid "divers"
+msgstr "diverso"
+
+msgid "None"
+msgstr "Ninguno"
+
+msgid "OK"
+msgstr "OK"
+
+msgid "Language:"
+msgstr "Idioma:"
+
+msgid "Skip to movie details for single result:"
+msgstr "Saltar a detalles de la película para un solo resultado:"
+
+msgid "Yellow key for TMDB infos in EPGs:"
+msgstr "Botón amarillo para información TMDB en EPG:"
+
+msgid "Cover resolution:"
+msgstr "Resolución de la carátula:"
+
+msgid "Backdrop resolution:"
+msgstr "Resolución del fondo:"
+
+msgid "Overview"
+msgstr "Resumen"
+
+msgid "Exit"
+msgstr "Salir"
+
+msgid "Details"
+msgstr "Detalles"
+
+msgid "Edit search"
+msgstr "Editar búsqueda"
+
+msgid "more ..."
+msgstr "más..."
+
+msgid "Show details"
+msgstr "Mostrar detalles"
+
+msgid "Details down"
+msgstr "Detalles abajo"
+
+msgid "Details up"
+msgstr "Detalles arriba"
+
+msgid "Current movies in cinemas"
+msgstr "Películas actuales en cartelera"
+
+msgid "No search string specified."
+msgstr "No se ha especificado una cadena de búsqueda."
+
+msgid "Looking up: %s ..."
+msgstr "Buscando: %s ..."
+
+msgid "page"
+msgstr "página"
+
+msgid "No results for: %s"
+msgstr "Sin resultados para: %s"
+
+msgid "Upcoming movies"
+msgstr "Próximos estrenos"
+
+msgid "Popular movies"
+msgstr "Películas más vistas"
+
+msgid "Similar movies"
+msgstr "Películas similares"
+
+msgid "Recommendations"
+msgstr "Recomendaciones"
+
+msgid "Best rated movies"
+msgstr "Películas mejor valoradas"
+
+msgid "Search for Movie:"
+msgstr "Buscar película:"
+
+msgid "Movie Details"
+msgstr "Detalles de la película"
+
+msgid "Genre:"
+msgstr "Género:"
+
+msgid "Votes:"
+msgstr "Votos:"
+
+msgid "Runtime:"
+msgstr "Duración:"
+
+msgid "Year:"
+msgstr "Año:"
+
+msgid "Countries:"
+msgstr "Países:"
+
+msgid "Director:"
+msgstr "Director:"
+
+msgid "Author:"
+msgstr "Autor:"
+
+msgid "Studio:"
+msgstr "Estudio:"
+
+msgid "Crew"
+msgstr "Reparto"
+
+msgid "Selection up"
+msgstr "Selección arriba"
+
+msgid "Selection down"
+msgstr "Selección abajo"
+
+msgid "Page up"
+msgstr "Página arriba"
+
+msgid "Page down"
+msgstr "Página abajo"
+
+msgid "Search"
+msgstr "Buscar"
+
+msgid "Videos"
+msgstr "Videos"
+
+msgid "Save movie description"
+msgstr "Guardar descripción de la película"
+
+msgid "Delete movie EIT file"
+msgstr "Borrar archivo EIT de la película"
+
+msgid "Save movie cover"
+msgstr "Guardar carátula de película"
+
+msgid "Save movie backdrop"
+msgstr "Guardar fondo de película"
+
+msgid "File operation results:"
+msgstr "Resultado de las operaciones de archivo:"
+
+msgid "Cover saved."
+msgstr "Carátula guardada."
+
+msgid "No cover available"
+msgstr "Carátula no disponible"
+
+msgid "Backdrop saved."
+msgstr "Fondo guardado."
+
+msgid "No backdrop available"
+msgstr "Fondo no disponible"
+
+msgid "Movie description saved."
+msgstr "Descripción de la película guardada."
+
+msgid "No movie description available"
+msgstr "Descripción de la película no disponible"
+
+msgid "EIT file deleted."
+msgstr "Fichero EIT borrado."
+
+msgid "No EIT file available"
+msgstr "Fichero EIT no disponible"
+
+msgid "No search provider registered."
+msgstr "Ningún proveedor de búsqueda registrado."
+
+msgid "TMDB videos"
+msgstr "Video TMDB "
+
+msgid "Please select a video"
+msgstr "Seleccione un vídeo"
+
+msgid "Person Details"
+msgstr "Datos personales"
+
+msgid "Popularity"
+msgstr "Popularidad"
+
+msgid "Known for:"
+msgstr "Conocido por:"
+
+msgid "Movie"
+msgstr "Película"
+
+msgid "Series"
+msgstr "Serie"
+
+msgid "Person"
+msgstr "Persona"
+
+msgid "TMDBCockpit"
+msgstr "TMDBCockpit"
+
+msgid "Access TMDB movie infos"
+msgstr "Acceder a información sobre películas de TMDB"
diff --git a/po/it.po b/po/it.po
new file mode 100644
index 0000000..a0d4270
--- /dev/null
+++ b/po/it.po
@@ -0,0 +1,258 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: TMDBCockpit\n"
+"POT-Creation-Date: 2024-03-05 22:28+0100\n"
+"PO-Revision-Date: 2024-03-05 22:32+0100\n"
+"Last-Translator: Dario Croci \n"
+"Language-Team: \n"
+"Language: it_IT\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=n != 1;\n"
+"X-Generator: Poedit 3.4.2\n"
+"X-Poedit-Basepath: ../../../../../../../../..\n"
+"X-Poedit-SearchPath-0: .\n"
+
+msgid "TMDB Infos ..."
+msgstr "Info TMDB..."
+
+msgid "Save movie description"
+msgstr "Salvare descrizione film"
+
+msgid "Save movie cover"
+msgstr "Salvare cover film"
+
+msgid "Save movie backdrop"
+msgstr "Salvare sfondo film"
+
+msgid "Save movie backdrop as cover"
+msgstr "Salvare sfondo film come cover"
+
+msgid "TMDB cover/backdrop"
+msgstr "Cover/backdrop TMDB"
+
+msgid "Please select a function"
+msgstr "Selezionare una funzione"
+
+msgid "Cover saved."
+msgstr "Cover salvata."
+
+msgid "No cover available"
+msgstr "Nessuna cover disponibile"
+
+msgid "Backdrop saved."
+msgstr "Sfondo salvato."
+
+msgid "No backdrop available"
+msgstr "Nessuno sfondo disponibile"
+
+msgid "Movie description saved."
+msgstr "Descrizione film salvata."
+
+msgid "No movie description available"
+msgstr "Nessuna descrizione disponibile"
+
+msgid "Various"
+msgstr "Varie"
+
+msgid "Seasons"
+msgstr "Stagioni"
+
+msgid "Episodes"
+msgstr "Episodi"
+
+msgid "Season"
+msgstr "Stagione"
+
+msgid "female"
+msgstr "Femminile"
+
+msgid "male"
+msgstr "Maschile"
+
+msgid "divers"
+msgstr "Diverso"
+
+msgid "not specified"
+msgstr "Non specificato"
+
+msgid "OK"
+msgstr "Ok"
+
+msgid "Cancel"
+msgstr "Annullare"
+
+msgid "Language:"
+msgstr "Lingua:"
+
+msgid "Skip to movie details for single result:"
+msgstr "Mostrare dettagli film su riscontro singolo:"
+
+msgid "Yellow key for TMDB infos in EPGs:"
+msgstr "Tasto giallo per info TMDB in EPG:"
+
+msgid "Cover resolution:"
+msgstr "Risoluzione cover:"
+
+msgid "Backdrop resolution:"
+msgstr "Risoluzione sfondi:"
+
+msgid "Player for trailers:"
+msgstr "Player per trailers:"
+
+msgid "Overview"
+msgstr "Panoramica"
+
+msgid "Details"
+msgstr "Dettagli"
+
+msgid "Edit search"
+msgstr "Modificare ricerca"
+
+msgid "more ..."
+msgstr "Altro ..."
+
+msgid "Show details"
+msgstr "Mostrare dettagli"
+
+msgid "Exit"
+msgstr "Uscire"
+
+msgid "Up"
+msgstr "Su"
+
+msgid "Down"
+msgstr "Giù"
+
+msgid "Details down"
+msgstr "Dettagli giù"
+
+msgid "Details up"
+msgstr "Dettagli su"
+
+msgid "Setup"
+msgstr "Impostazioni"
+
+msgid "Current movies in cinemas"
+msgstr "Film attualmente nei cinema"
+
+msgid "No search string specified."
+msgstr "Stringa di ricerca non specificata."
+
+msgid "Looking up: %s ..."
+msgstr "Ricerca: %s..."
+
+msgid "page"
+msgstr "Pagina"
+
+msgid "No results for: %s"
+msgstr "Nessun risultato per: %s"
+
+msgid "Upcoming movies"
+msgstr "Film in arrivo"
+
+msgid "Popular movies"
+msgstr "Film popolari"
+
+msgid "Similar movies"
+msgstr "Film simili"
+
+msgid "Recommendations"
+msgstr "Raccomandazioni"
+
+msgid "Best rated movies"
+msgstr "Film più votati"
+
+msgid "TMDB categories"
+msgstr "Categorie TMDB"
+
+msgid "Please select a category"
+msgstr "Selezionare una categoria"
+
+msgid "Search for Movie:"
+msgstr "Ricerca film:"
+
+msgid "Movie Details"
+msgstr "Dettagli film"
+
+msgid "Genre:"
+msgstr "Genere:"
+
+msgid "Votes:"
+msgstr "Voti:"
+
+msgid "Runtime:"
+msgstr "Durata:"
+
+msgid "Year:"
+msgstr "Anno:"
+
+msgid "Countries:"
+msgstr "Nazioni:"
+
+msgid "Director:"
+msgstr "Regista:"
+
+msgid "Author:"
+msgstr "Autore:"
+
+msgid "Studio:"
+msgstr "Studio:"
+
+msgid "Crew"
+msgstr "Team"
+
+msgid "Selection up"
+msgstr "Selezione su"
+
+msgid "Selection down"
+msgstr "Selezione giù"
+
+msgid "Page up"
+msgstr "Pagina su"
+
+msgid "Page down"
+msgstr "Pagina giù"
+
+msgid "Search"
+msgstr "Cercare"
+
+msgid "Videos"
+msgstr "Video"
+
+msgid "No search provider registered."
+msgstr "Nessun provider di ricerca registrato."
+
+msgid "TMDB videos"
+msgstr "Video TMDB"
+
+msgid "Please select a video"
+msgstr "Selezionare un video"
+
+msgid "Person Details"
+msgstr "Dettagli persona"
+
+msgid "Popularity"
+msgstr "Popolarità"
+
+msgid "Known for:"
+msgstr "Noto per:"
+
+msgid "Movie"
+msgstr "Film"
+
+msgid "Series"
+msgstr "Serie"
+
+msgid "Person"
+msgstr "Persona"
+
+msgid "min"
+msgstr "min"
+
+msgid "TMDBCockpit"
+msgstr "TMDBCockpit"
+
+msgid "Access TMDB movie infos"
+msgstr "Accesso a informazioni film TMDB"
diff --git a/po/nl.po b/po/nl.po
new file mode 100644
index 0000000..6cc9e86
--- /dev/null
+++ b/po/nl.po
@@ -0,0 +1,329 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: TMDBCockpit\n"
+"POT-Creation-Date: 2023-05-15 11:57+0200\n"
+"PO-Revision-Date: 2023-05-15 12:01+0200\n"
+"Last-Translator: dream-alpha\n"
+"Language-Team: \n"
+"Language: nl\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"X-Generator: Poedit 3.2.2\n"
+"X-Poedit-KeywordsList: _;gettext;gettext_noop\n"
+"X-Poedit-Basepath: ../src\n"
+"X-Poedit-SourceCharset: UTF-8\n"
+"X-Poedit-SearchPath-0: .\n"
+
+msgid "Plugin"
+msgstr "Plugin"
+
+msgid "Version"
+msgstr "Versie"
+
+msgid "Copyright"
+msgstr "Copyright"
+
+msgid "License"
+msgstr "Licentie"
+
+msgid "End"
+msgstr "Einde"
+
+msgid "Stop playback"
+msgstr "Stop afspelen"
+
+msgid "Skip forward"
+msgstr "Spring vooruit"
+
+msgid "Skip backward"
+msgstr "Spring terug"
+
+msgid "EPG Info"
+msgstr "EPG Info"
+
+msgid "Do you want to resume playback at position: %d:%02d:%02d?"
+msgstr "Wilt u het afspelen hervatten op posistie: %d:%02d:%02d?"
+
+msgid "Cancel"
+msgstr "Afbreken"
+
+msgid "Save"
+msgstr "Opslaan"
+
+msgid "Defaults"
+msgstr "Standaard waarden"
+
+msgid "Really close without saving settings?"
+msgstr "Afsluiten zonder op te slaan?"
+
+msgid "Setup"
+msgstr "Instellingen"
+
+msgid "Loading default settings will overwrite all settings, really load them?"
+msgstr "Het laden van stadaard waarden overschrijft alle instellingen, doorgaan?"
+
+msgid "Some changes require a GUI restart"
+msgstr "Enkele aanpassingen vereisen een GUI herstart"
+
+msgid "Restart GUI now?"
+msgstr "GUI herstarten?"
+
+msgid "Bookmarks"
+msgstr "Bookmarks"
+
+msgid "Select directory"
+msgstr "Selecteer directory"
+
+msgid "Path does not exist"
+msgstr "Path bestaat niet"
+
+msgid "Close"
+msgstr "Sluiten"
+
+msgid "Hide"
+msgstr "Verbergen"
+
+msgid "Cancelling, please wait"
+msgstr "Afbreken, wachten aub"
+
+msgid "Processing"
+msgstr "Verwerken"
+
+msgid "of"
+msgstr "of"
+
+msgid "Done"
+msgstr "Klaar"
+
+msgid "Cancelled"
+msgstr "Afgebroken"
+
+msgid "Please wait"
+msgstr "Wachten aub"
+
+msgid "min"
+msgstr "min"
+
+msgid "TMDB Infos ..."
+msgstr "TMDB Info ..."
+
+msgid "Various"
+msgstr "Verschilende"
+
+msgid "Seasons"
+msgstr "Seizoenen"
+
+msgid "Episodes"
+msgstr "Afleveringen"
+
+msgid "Season"
+msgstr "Seizoen"
+
+msgid "female"
+msgstr "vrouwelijk"
+
+msgid "male"
+msgstr "mannelijk"
+
+msgid "divers"
+msgstr "divers"
+
+msgid "None"
+msgstr "Geen"
+
+msgid "OK"
+msgstr "OK"
+
+msgid "Language:"
+msgstr "Taal"
+
+msgid "Skip to movie details for single result:"
+msgstr "Ga naar film details voor enkel resultaat:"
+
+msgid "Yellow key for TMDB infos in EPGs:"
+msgstr "Gele toets voor TMBD info in de EPG:"
+
+msgid "Cover resolution:"
+msgstr "Voorkant resolutie"
+
+msgid "Backdrop resolution:"
+msgstr "Achtergrond resolutie"
+
+msgid "Overview"
+msgstr "Overzicht"
+
+msgid "Exit"
+msgstr "Verlaten"
+
+msgid "Details"
+msgstr "Details"
+
+msgid "Edit search"
+msgstr "Aanpassen zoeken"
+
+msgid "more ..."
+msgstr "meer ..."
+
+msgid "Show details"
+msgstr "Toon details"
+
+msgid "Details down"
+msgstr "Details omhoog"
+
+msgid "Details up"
+msgstr "Details omlaag"
+
+msgid "Current movies in cinemas"
+msgstr "Films die draaien in de bioscopen"
+
+msgid "No search string specified."
+msgstr "Geen zoek tekst opgegeven"
+
+msgid "Looking up: %s ..."
+msgstr "Looking up: %s ..."
+
+msgid "page"
+msgstr "pagina"
+
+msgid "No results for: %s"
+msgstr "Geen resultaat voor: %s"
+
+msgid "Upcoming movies"
+msgstr "Aankomde films"
+
+msgid "Popular movies"
+msgstr "Populiere films"
+
+msgid "Similar movies"
+msgstr "Gelijkwaardige films"
+
+msgid "Recommendations"
+msgstr "Aanbevolen"
+
+msgid "Best rated movies"
+msgstr "Beste beoordeelde films"
+
+msgid "Search for Movie:"
+msgstr "Zoek voor film:"
+
+msgid "Movie Details"
+msgstr "Film details"
+
+msgid "Genre:"
+msgstr "Genre:"
+
+msgid "Votes:"
+msgstr "Stemmen:"
+
+msgid "Runtime:"
+msgstr "Lengte:"
+
+msgid "Year:"
+msgstr "Jaar:"
+
+msgid "Countries:"
+msgstr "Landen:"
+
+msgid "Director:"
+msgstr ""
+
+msgid "Author:"
+msgstr "Auteur:"
+
+msgid "Studio:"
+msgstr "Studio:"
+
+msgid "Crew"
+msgstr "Ploeg"
+
+msgid "Selection up"
+msgstr "Keuze omhoog"
+
+msgid "Selection down"
+msgstr "Kleuze omlaag"
+
+msgid "Page up"
+msgstr "Pagina omhoog"
+
+msgid "Page down"
+msgstr "Pagina omlaag"
+
+msgid "Search"
+msgstr "Zoek"
+
+msgid "Videos"
+msgstr "Videos"
+
+msgid "Save movie description"
+msgstr "Sla film beschrijving op"
+
+msgid "Delete movie EIT file"
+msgstr "Verwijder film EIT file"
+
+msgid "Save movie cover"
+msgstr "Sla film omslag op"
+
+msgid "Save movie backdrop"
+msgstr "Sla film achtergrond op"
+
+msgid "File operation results:"
+msgstr "Resultaat van bestand bewerking"
+
+msgid "Cover saved."
+msgstr "Onslag opgeslagen"
+
+msgid "No cover available"
+msgstr "Geen omslag beschikbaar"
+
+msgid "Backdrop saved."
+msgstr "Achtergrond opgeslagen"
+
+msgid "No backdrop available"
+msgstr "Geen achtergrond beschikbaar"
+
+msgid "Movie description saved."
+msgstr "Omschrijving film opgeslagen."
+
+msgid "No movie description available"
+msgstr "Geen film omschrijving beschikbaar"
+
+msgid "EIT file deleted."
+msgstr "EIT bestand verwijderd"
+
+msgid "No EIT file available"
+msgstr "Geen EIT bestand beschikbaar"
+
+msgid "No search provider registered."
+msgstr "Geen zoek leverancier geregistreerd"
+
+msgid "TMDB videos"
+msgstr "TMDM films"
+
+msgid "Please select a video"
+msgstr "Selecteer een film aub"
+
+msgid "Person Details"
+msgstr "Persoonlijke gegevens"
+
+msgid "Popularity"
+msgstr "Populariteit"
+
+msgid "Known for:"
+msgstr "Bekend van:"
+
+msgid "Movie"
+msgstr "Film"
+
+msgid "Series"
+msgstr "Series"
+
+msgid "Person"
+msgstr "Persoon"
+
+msgid "TMDBCockpit"
+msgstr "TMDBCocpit"
+
+msgid "Access TMDB movie infos"
+msgstr "Benader TMDB film informatie"
diff --git a/po/ru.po b/po/ru.po
new file mode 100644
index 0000000..a601bf0
--- /dev/null
+++ b/po/ru.po
@@ -0,0 +1,368 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: TMDBCockpit\n"
+"POT-Creation-Date: 2023-01-30 16:17+0100\n"
+"PO-Revision-Date: 2023-01-30 16:20+0100\n"
+"Last-Translator: dream-alpha\n"
+"Language-Team: \n"
+"Language: ru\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"X-Generator: Poedit 3.1\n"
+"X-Poedit-KeywordsList: _;gettext;gettext_noop\n"
+"X-Poedit-Basepath: ../src\n"
+"X-Poedit-SourceCharset: UTF-8\n"
+"X-Poedit-SearchPath-0: .\n"
+
+msgid "Author:"
+msgstr "Автор:"
+
+msgid "Backdrop saved!"
+msgstr "Фон сохранен!"
+
+msgid "Best rated movies"
+msgstr "Лучшие рейтинговые фильмы"
+
+msgid "Bookmarks"
+msgstr "Закладки"
+
+msgid "Cancel"
+msgstr "Отмена"
+
+msgid "Cancelled"
+msgstr "Отменено"
+
+msgid "Cancelling, please wait"
+msgstr "Отмена, пожалуйста, подождите"
+
+msgid "Check SSL certificate:"
+msgstr "Проверьте SSL-сертификат:"
+
+msgid "Close"
+msgstr "Закрыть"
+
+msgid "Copyright"
+msgstr "Авторские права"
+
+msgid "Countries:"
+msgstr "Страны:"
+
+msgid "Cover saved!"
+msgstr "Постер сохранён!"
+
+msgid "Cover saved."
+msgstr "Постер сохранён."
+
+msgid "Crew"
+msgstr "Состав"
+
+msgid "Current movies in cinemas"
+msgstr "Текущие фильмы в кинотеатрах"
+
+msgid "Defaults"
+msgstr "По умолчанию"
+
+msgid "Delete movie EIT file"
+msgstr "Удалить EIT-файл"
+
+msgid "Details"
+msgstr "Подробно"
+
+msgid "Details down"
+msgstr "Подробно вниз"
+
+msgid "Details up"
+msgstr "Подробно вверх"
+
+msgid "Director:"
+msgstr "Режиссёр:"
+
+msgid "Do you want to resume playback at position: %d:%02d:%02d?"
+msgstr "Вы хотите возобновить воспроизведение с позиции: %d:%02d:%02d?"
+
+msgid "Done"
+msgstr "Готово"
+
+msgid "EIT file deleted!"
+msgstr "EIT-файл удален!"
+
+msgid "EIT file deleted."
+msgstr "EIT-файл удален."
+
+msgid "EPG Info"
+msgstr "EPG инфо"
+
+msgid "Edit search"
+msgstr "Редактировать поиск"
+
+msgid "End"
+msgstr "Конец"
+
+msgid "Episodes"
+msgstr "Эпизоды"
+
+msgid "Exit"
+msgstr "Выход"
+
+msgid "Genre:"
+msgstr "Жанр:"
+
+msgid "Hide"
+msgstr "Скрыть"
+
+msgid "Known for:"
+msgstr "Известен из:"
+
+msgid "Language:"
+msgstr "Язык:"
+
+msgid "License"
+msgstr "Лицензия"
+
+msgid "Loading default settings will overwrite all settings, really load them?"
+msgstr "Загрузка настроек по умолчанию перезапишет все настройки, действительно загрузить их?"
+
+msgid "Loading..."
+msgstr "Загрузка..."
+
+msgid "Movie"
+msgstr "Фильм"
+
+msgid "Movie description saved!"
+msgstr "Описание фильма сохранено!"
+
+msgid "OK"
+msgstr "Ok"
+
+msgid "Overwrite key yellow for TMDB infos in EPGs:"
+msgstr "Перезаписать желтую клавишу для информации TMDB в EPG:"
+
+msgid "Page down"
+msgstr "Листать вниз"
+
+msgid "Page up"
+msgstr "Листать вверх"
+
+msgid "Path does not exist"
+msgstr "Путь не существует"
+
+msgid "Please wait"
+msgstr "Пожалуйста подождите"
+
+msgid "Plugin"
+msgstr "Плагин"
+
+msgid "Popular movies"
+msgstr "Популярные фильмы"
+
+msgid "Popularity"
+msgstr "Популярность"
+
+msgid "Processing"
+msgstr "В обработке"
+
+msgid "Really close without saving settings?"
+msgstr "Закрыть без сохранения настроек?"
+
+msgid "Recommendations"
+msgstr "Рекомендуемые фильмы"
+
+msgid "Restart GUI now?"
+msgstr "Перезапустить GUI сейчас?"
+
+msgid "Runtime:"
+msgstr "Время:"
+
+msgid "Save"
+msgstr "Сохранить"
+
+msgid "Save movie backdrop"
+msgstr "Сохранить фон"
+
+msgid "Save movie cover"
+msgstr "Сохранить постер"
+
+msgid "Save movie description"
+msgstr "Сохранить описание"
+
+msgid "Search for Movie:"
+msgstr "Поиск фильма:"
+
+msgid "Season"
+msgstr "Сезон"
+
+msgid "Seasons"
+msgstr "Сезоны"
+
+msgid "Select directory"
+msgstr "Выбор каталога"
+
+msgid "Selection down"
+msgstr "вниз"
+
+msgid "Selection up"
+msgstr "вверх"
+
+msgid "Series"
+msgstr "Серии"
+
+msgid "Setup"
+msgstr "Настройки"
+
+msgid "Show details"
+msgstr "Показать детали"
+
+msgid "Show details if single result:"
+msgstr "Показать подробности, если один результат:"
+
+msgid "Similar movies"
+msgstr "Похожие фильмы"
+
+msgid "Skip backward"
+msgstr "Перейти назад"
+
+msgid "Skip forward"
+msgstr "Перейти вперёд"
+
+msgid "Some changes require a GUI restart"
+msgstr "Некоторые изменения требуют перезапуска GUI"
+
+msgid "Stop playback"
+msgstr "Остановить воспроизведение"
+
+msgid "Studio:"
+msgstr "Студия:"
+
+msgid "TMDB Infos ..."
+msgstr "TMDB инфо "
+
+msgid "TMDB:"
+msgstr "TMDB:"
+
+msgid "TMDB: No results for %s"
+msgstr "Нет результатов для %s"
+
+msgid "TMDB: No results found, or does not respond!"
+msgstr "Результатов не найдено или сервер не отвечает!"
+
+msgid "TMDB: Results for %s"
+msgstr "Результаты для: %s"
+
+msgid "TMDB: Server does not respond!"
+msgstr "Сервер не отвечает!"
+
+msgid "Try to find %s in tmdb ..."
+msgstr "Попробуйте найти %s в TMDB ..."
+
+msgid "Upcoming movies"
+msgstr "Предстоящие фильмы"
+
+msgid "Various"
+msgstr "Разное"
+
+msgid "Version"
+msgstr "Версия"
+
+msgid "Votes:"
+msgstr "Голосов:"
+
+msgid "Year:"
+msgstr "Год:"
+
+msgid "divers"
+msgstr "divers"
+
+msgid "female"
+msgstr "женский"
+
+msgid "male"
+msgstr "мужской"
+
+msgid "min"
+msgstr "мин"
+
+msgid "more ..."
+msgstr "больше ..."
+
+msgid "of"
+msgstr "из"
+
+msgid "page "
+msgstr "страница "
+
+msgid "Skip to movie details for single result:"
+msgstr "Перейти к информации для найденого фильма"
+
+msgid "Yellow key for TMDB infos in EPGs:"
+msgstr "Вывод инфо TMDB жёлтой кнопкой в EPG:"
+
+msgid "Cover resolution:"
+msgstr "Размер постера:"
+
+msgid "Backdrop resolution:"
+msgstr "Размер фоновой заставки:"
+
+msgid "No movie description available"
+msgstr "Описание фильма недоступно"
+
+msgid "Movie description saved."
+msgstr "Описание фильма сохранено."
+
+msgid "No backdrop available"
+msgstr "Нет фоновой заставки"
+
+msgid "Backdrop saved."
+msgstr "Фоновая заставка сохранена."
+
+msgid "No cover available"
+msgstr "Постер недоступен"
+
+msgid "Person"
+msgstr "Персона"
+
+msgid "TMDB: Server does not respond."
+msgstr "Сервер не отвечает."
+
+msgid "File operation results:"
+msgstr "Результат файловых операций:"
+
+msgid "No EIT file available"
+msgstr "EIT-файл недоступен"
+
+msgid "Use internal TMDB API key:"
+msgstr "Используйте внутренний ключ API TMDB:"
+
+msgid "External TMDB API key is not available in:"
+msgstr "Внешний API-ключ TMDB недоступен в:"
+
+msgid "Overview"
+msgstr "Обзор"
+
+msgid "Looking up: %s ..."
+msgstr "Поиск: %s ..."
+
+msgid "No search string specified."
+msgstr "Строка поиска не указана."
+
+msgid "No results for: %s"
+msgstr "Нет результатов для: %s"
+
+msgid "Movie Details"
+msgstr "Подробности"
+
+msgid "Videos"
+msgstr "Видео"
+
+msgid "TMDB videos"
+msgstr "TMDB видео"
+
+msgid "Please select a video"
+msgstr "Пожалуйста, выберите видео"
+
+msgid "Person Details"
+msgstr "Личные данные"
+
+msgid "None"
+msgstr "Нет данных"
diff --git a/po/tr.po b/po/tr.po
new file mode 100644
index 0000000..7e048f7
--- /dev/null
+++ b/po/tr.po
@@ -0,0 +1,268 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: TMDBCockpit\n"
+"POT-Creation-Date: 2023-02-23 22:06+0300\n"
+"PO-Revision-Date: 2023-02-24 02:15+0300\n"
+"Last-Translator: audi06_19 \n"
+"Language-Team: \n"
+"Language: tr\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"X-Generator: Poedit 3.2.2\n"
+"X-Poedit-KeywordsList: _;gettext;gettext_noop\n"
+"X-Poedit-Basepath: ../src\n"
+"X-Poedit-SourceCharset: UTF-8\n"
+"X-Poedit-SearchPath-0: .\n"
+
+msgid "Setup"
+msgstr "Kurulum"
+
+msgid "OK"
+msgstr "OK"
+
+msgid "Cancel"
+msgstr "İptal"
+
+msgid "Language:"
+msgstr "Dil:"
+
+msgid "Skip to movie details for single result:"
+msgstr "Tek bir sonuç için film ayrıntılarına geç:"
+
+msgid "Yellow key for TMDB infos in EPGs:"
+msgstr "EPG'lerde TMDB bilgileri için sarı tuş:"
+
+msgid "Cover resolution:"
+msgstr "Kapak çözünürlüğü:"
+
+msgid "Backdrop resolution:"
+msgstr "Arka plan çözünürlüğü:"
+
+msgid "Use internal TMDB API key:"
+msgstr "Dahili TMDB API anahtarını kullan:"
+
+msgid "Overview"
+msgstr "Genel Bakış"
+
+msgid "Exit"
+msgstr "Çıkış"
+
+msgid "Details"
+msgstr "Detaylar"
+
+msgid "Edit search"
+msgstr "Aramayı düzenle"
+
+msgid "more ..."
+msgstr "daha fazla ..."
+
+msgid "Show details"
+msgstr "Ayrıntıları göster"
+
+msgid "Details down"
+msgstr "Ayrıntılar aşağı"
+
+msgid "Details up"
+msgstr "Ayrıntılar yukarı"
+
+msgid "External TMDB API key is not available in:"
+msgstr "Harici TMDB API anahtarı şurada mevcut değil:"
+
+msgid "Looking up: %s ..."
+msgstr "Yukarı bakılıyor: %s ..."
+
+msgid "No search string specified."
+msgstr "Arama dizisi belirtilmedi."
+
+msgid "page"
+msgstr "sayfa"
+
+msgid "No results for: %s"
+msgstr "Şunun için sonuç yok: %s"
+
+msgid "TMDB Infos ..."
+msgstr "TMDB Bilgileri ..."
+
+msgid "Current movies in cinemas"
+msgstr "Sinemalardaki güncel filmler"
+
+msgid "Upcoming movies"
+msgstr "Yaklaşan filmler"
+
+msgid "Popular movies"
+msgstr "Popüler filmler"
+
+msgid "Similar movies"
+msgstr "Benzer filmler"
+
+msgid "Recommendations"
+msgstr "Öneriler"
+
+msgid "Best rated movies"
+msgstr "En çok oy alan filmler"
+
+msgid "Search for Movie:"
+msgstr "Film Ara:"
+
+msgid "Movie Details"
+msgstr "Film Ayrıntıları"
+
+msgid "Genre:"
+msgstr "Tür:"
+
+msgid "Votes:"
+msgstr "Oylar:"
+
+msgid "Runtime:"
+msgstr "Süre:"
+
+msgid "Year:"
+msgstr "Yıl:"
+
+msgid "Countries:"
+msgstr "Ülkeler:"
+
+msgid "Director:"
+msgstr "Yönetmen:"
+
+msgid "Author:"
+msgstr "Yazar:"
+
+msgid "Studio:"
+msgstr "Stüdyo:"
+
+msgid "Crew"
+msgstr "Ekip"
+
+msgid "Seasons"
+msgstr "Mevsimler"
+
+msgid "Selection up"
+msgstr "Seçim yukarı"
+
+msgid "Selection down"
+msgstr "Seçim aşağı"
+
+msgid "Page up"
+msgstr "Sayfa yukarı"
+
+msgid "Page down"
+msgstr "Sayfa aşağı"
+
+msgid "Videos"
+msgstr "Videolar"
+
+msgid "Save movie description"
+msgstr "Film açıklamasını kaydet"
+
+msgid "Delete movie EIT file"
+msgstr "Film EIT dosyasını sil"
+
+msgid "Save movie cover"
+msgstr "Film kapağını kaydet"
+
+msgid "Save movie backdrop"
+msgstr "Film arka planını kaydet"
+
+msgid "File operation results:"
+msgstr "Dosya işlemi sonuçları:"
+
+msgid "Cover saved."
+msgstr "Kapak kaydedildi."
+
+msgid "No cover available"
+msgstr "Kapak yok"
+
+msgid "Backdrop saved."
+msgstr "Arka plan kaydedildi."
+
+msgid "No backdrop available"
+msgstr "Arka plan yok"
+
+msgid "Movie description saved."
+msgstr "Film açıklaması kaydedildi."
+
+msgid "No movie description available"
+msgstr "Film açıklaması yok"
+
+msgid "EIT file deleted."
+msgstr "EIT dosyası silindi."
+
+msgid "No EIT file available"
+msgstr "EIT dosyası yok"
+
+msgid "TMDB videos"
+msgstr "TMDB videoları"
+
+msgid "Please select a video"
+msgstr "Lütfen bir video seçin"
+
+msgid "Person Details"
+msgstr "Kişi Ayrıntıları"
+
+msgid "Popularity"
+msgstr "Popülerlik"
+
+msgid "Known for:"
+msgstr "Tanınır:"
+
+msgid "Movie"
+msgstr "Film"
+
+msgid "Series"
+msgstr "Diziler"
+
+msgid "Person"
+msgstr "Kişi"
+
+msgid "min"
+msgstr "dk"
+
+msgid "Various"
+msgstr "Çeşitli"
+
+msgid "Episodes"
+msgstr "Bölümler"
+
+msgid "Season"
+msgstr "Sezon"
+
+msgid "female"
+msgstr "kadın"
+
+msgid "male"
+msgstr "erkek"
+
+msgid "divers"
+msgstr ""
+
+msgid "None"
+msgstr "Yok"
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/setup.cfg b/setup.cfg
new file mode 100644
index 0000000..968fd5c
--- /dev/null
+++ b/setup.cfg
@@ -0,0 +1,9 @@
+[flake8]
+ignore = W191, W503, E117, E203, E242, E501
+
+[pycodestyle]
+count = False
+ignore = W191, W503, E117, E203, E242
+
+max-line-length = 320
+statistics = True
diff --git a/src/ConfigInit.py b/src/ConfigInit.py
new file mode 100644
index 0000000..c77a2e8
--- /dev/null
+++ b/src/ConfigInit.py
@@ -0,0 +1,46 @@
+#!/usr/bin/python
+# coding=utf-8
+#
+# Copyright (C) 2018-2024 by dream-alpha
+#
+# In case of reuse of this source code please do not remove this copyright.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# For more information on the GNU General Public License see:
+# .
+
+
+from Components.config import config, ConfigYesNo, ConfigSelection, ConfigSubsection
+from Components.Language import language
+from .LanguageSelection import LanguageSelection
+from .Debug import logger, setLogLevel, log_levels, initLogging
+
+
+class ConfigInit(LanguageSelection):
+
+ def __init__(self):
+ logger.info("...")
+ LanguageSelection.__init__(self)
+ lang = language.getActiveLanguage()
+ logger.debug("lang: %s", lang)
+ lang_choices = self.getLangChoices(lang)
+ config.plugins.tmdb = ConfigSubsection()
+ config.plugins.tmdb.debug_log_level = ConfigSelection(default="INFO", choices=list(log_levels.keys()))
+ config.plugins.tmdb.cover_size = ConfigSelection(default="original", choices=["w92", "w185", "w500", "original"])
+ config.plugins.tmdb.backdrop_size = ConfigSelection(default="original", choices=["w300", "w780", "w1280", "original"])
+ config.plugins.tmdb.lang = ConfigSelection(default=lang[:2], choices=lang_choices)
+ config.plugins.tmdb.skip_to_movie = ConfigYesNo(default=True)
+ config.plugins.tmdb.key_yellow = ConfigYesNo(default=True)
+
+ config.plugins.tmdb.trailer_player = ConfigSelection(default="MediaPortal", choices=["MediaPortal", "MyTube"])
+ setLogLevel(log_levels[config.plugins.tmdb.debug_log_level.value])
+ initLogging()
diff --git a/src/Debug.py b/src/Debug.py
new file mode 100644
index 0000000..5045d56
--- /dev/null
+++ b/src/Debug.py
@@ -0,0 +1,53 @@
+#!/usr/bin/python
+# coding=utf-8
+#
+# Copyright (C) 2018-2024 by dream-alpha
+#
+# In case of reuse of this source code please do not remove this copyright.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# For more information on the GNU General Public License see:
+# .
+
+
+import sys
+import logging
+from Components.config import config, ConfigSubsection, ConfigDirectory, ConfigSelection # noqa: F401, pylint: disable=W0611
+from .Version import ID, PLUGIN
+
+
+logger = None
+streamer = None
+format_string = ID + ": " + "%(levelname)s: %(filename)s: %(funcName)s: %(message)s"
+log_levels = {"ERROR": logging.ERROR, "INFO": logging.INFO, "DEBUG": logging.DEBUG}
+plugin = PLUGIN.lower()
+exec("config.plugins." + plugin + " = ConfigSubsection()") # noqa: F401, pylint: disable=W0122
+exec("config.plugins." + plugin + ".debug_log_level = ConfigSelection(default='INFO', choices=log_levels.keys())") # noqa: F401, pylint: disable=W0122
+
+
+def initLogging():
+ global logger
+ global streamer
+ if not logger:
+ logger = logging.getLogger(ID)
+ formatter = logging.Formatter(format_string)
+ streamer = logging.StreamHandler(sys.stdout)
+ streamer.setFormatter(formatter)
+ logger.addHandler(streamer)
+ logger.propagate = False
+ setLogLevel(log_levels[eval("config.plugins." + plugin + ".debug_log_level").value])
+
+
+def setLogLevel(level):
+ logger.setLevel(level)
+ streamer.setLevel(level)
+ logger.info("level: %s", level)
diff --git a/src/DelayTimer.py b/src/DelayTimer.py
new file mode 100644
index 0000000..c801f97
--- /dev/null
+++ b/src/DelayTimer.py
@@ -0,0 +1,53 @@
+#!/usr/bin/python
+# coding=utf-8
+#
+# Copyright (C) 2018-2024 by dream-alpha
+#
+# In case of reuse of this source code please do not remove this copyright.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# For more information on the GNU General Public License see:
+# .
+
+
+from enigma import eTimer
+
+
+timer_instances = []
+
+
+class DelayTimer():
+
+ def __init__(self, delay, function, *args):
+ if delay:
+ timer_instances.append(self)
+ self.timer = eTimer()
+ self.function = function
+ self.args = args
+ self.timer_conn = self.timer.timeout.connect(self.fire)
+ self.timer.start(delay, True)
+ else:
+ function(*args)
+
+ def fire(self):
+ timer_instances.remove(self)
+ self.function(*self.args)
+
+ def stop(self):
+ if self in timer_instances:
+ timer_instances.remove(self)
+ self.timer.stop()
+
+ @staticmethod
+ def stopAll():
+ for timer_instance in timer_instances:
+ timer_instance.timer.stop()
diff --git a/src/EpgSelection.py b/src/EpgSelection.py
new file mode 100644
index 0000000..5ddf204
--- /dev/null
+++ b/src/EpgSelection.py
@@ -0,0 +1,53 @@
+#!/usr/bin/python
+# coding=utf-8
+#
+# Copyright (C) 2018-2024 by dream-alpha
+#
+# In case of reuse of this source code please do not remove this copyright.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# For more information on the GNU General Public License see:
+# .
+
+
+from Screens.EpgSelection import EPGSelection
+from Components.ActionMap import ActionMap
+from .ScreenMain import ScreenMain
+from .__init__ import _
+
+
+original_init = None
+
+
+def initEPGSelection():
+ global original_init
+ if original_init is None:
+ original_init = EPGSelection.__init__
+ EPGSelection.__init__ = our_init
+
+
+def our_init(self, session, service, zapFunc=None, eventid=None, bouquetChangeCB=None, serviceChangeCB=None):
+ def yellow():
+ event_name = ""
+ current = self["list"].getCurrent()
+ if current and current[0]:
+ event_name = current[0].getEventName()
+ session.open(ScreenMain, event_name, 2)
+
+ original_init(self, session, service, zapFunc, eventid, bouquetChangeCB, serviceChangeCB)
+ self["tmdb_actions"] = ActionMap(
+ ["EPGSelectActions"],
+ {
+ "yellow": yellow,
+ }
+ )
+ self["key_yellow"].setText(_("TMDB Infos"))
diff --git a/src/FileUtils.py b/src/FileUtils.py
new file mode 100644
index 0000000..71cdf07
--- /dev/null
+++ b/src/FileUtils.py
@@ -0,0 +1,88 @@
+#!/usr/bin/python
+# coding=utf-8
+#
+# Copyright (C) 2018-2024 by dream-alpha
+#
+# In case of reuse of this source code please do not remove this copyright.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# For more information on the GNU General Public License see:
+# .
+
+
+import os
+from pipes import quote
+import glob
+from .Debug import logger
+
+
+def stripCutNumber(path):
+ filename, ext = os.path.splitext(path)
+ if len(filename) > 3:
+ if filename[-4] == "_" and filename[-3:].isdigit():
+ filename = filename[:-4]
+ path = filename + ext
+ return path
+
+
+def readFile(path):
+ data = ""
+ try:
+ with open(path, "r") as f:
+ data = f.read()
+ except Exception as e:
+ logger.info("path: %s, exception: %s", path, e)
+ return data
+
+
+def writeFile(path, data):
+ try:
+ with open(path, "w") as f:
+ f.write(data)
+ except Exception as e:
+ logger.error("path: %s, exception: %s", path, e)
+
+
+def deleteFile(path):
+ os.popen("rm %s" % quote(path)).read()
+
+
+def deleteFiles(path, clear=False):
+ for afile in glob.glob(path):
+ if clear:
+ writeFile(afile, "")
+ deleteFile(afile)
+
+
+def touchFile(path):
+ os.popen("touch %s" % quote(path)).read()
+
+
+def copyFile(src_path, dest_path):
+ os.popen("cp %s %s" % (quote(src_path), quote(dest_path))).read()
+
+
+def renameFile(src_path, dest_path):
+ os.popen("mv %s %s" % (quote(src_path), quote(dest_path))).read()
+
+
+def createDirectory(path):
+ os.popen("mkdir -p %s" % quote(path)).read()
+
+
+def createSymlink(src, dst):
+ logger.info("link: src: %s > %s", src, dst)
+ os.symlink(src, dst)
+
+
+def deleteDirectory(path):
+ os.popen("rm -rf %s" % quote(path)).read()
diff --git a/src/Json.py b/src/Json.py
new file mode 100644
index 0000000..9dbfc60
--- /dev/null
+++ b/src/Json.py
@@ -0,0 +1,63 @@
+#!/usr/bin/python
+# coding=utf-8
+#
+# Copyright (C) 2018-2024 by dream-alpha
+#
+# In case of reuse of this source code please do not remove this copyright.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# For more information on the GNU General Public License see:
+# .
+
+
+import six
+from .Debug import logger
+from .Utils import checkText
+
+
+class Json():
+ def __init__(self):
+ return
+
+ def parseJson(self, result, source, keys):
+ for key in keys:
+ self.parseJsonSingle(result, source, key)
+
+ def parseJsonSingle(self, result, source, key):
+ value = "None"
+ if key in source:
+ value = source[key]
+ if isinstance(value, six.text_type):
+ value = six.ensure_str(value)
+ if value is None:
+ value = "None"
+ result[key] = value
+
+ def parseJsonList(self, result, key, separator):
+ logger.info("result: %s, key: %s, separator: %s", result, key, separator)
+ alist = ""
+ if key in result:
+ logger.debug("key: %s", result[key])
+ for source in result[key]:
+ # logger.debug("source: %s", source)
+ text = checkText(source)
+ # logger.debug("checked text: %s", text)
+ text = six.ensure_str(text)
+ if alist and text:
+ alist += separator + " "
+ if text:
+ alist += text
+ logger.debug("alist: %s", alist)
+ result[key] = alist
+ else:
+ result[key] = ""
+ logger.debug("result[key]: %s", result[key])
diff --git a/src/LanguageSelection.py b/src/LanguageSelection.py
new file mode 100644
index 0000000..0b7d951
--- /dev/null
+++ b/src/LanguageSelection.py
@@ -0,0 +1,42 @@
+#!/usr/bin/python
+# coding=utf-8
+#
+# Copyright (C) 2018-2024 by dream-alpha
+#
+# In case of reuse of this source code please do not remove this copyright.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# For more information on the GNU General Public License see:
+# .
+
+
+from Components.language_cache import LANG_TEXT
+from .Debug import logger
+
+
+class LanguageSelection():
+
+ def __init__(self):
+ return
+
+ def getLangChoices(self, sys_lang):
+ logger.info("sys_lang: %s", sys_lang)
+ if sys_lang == "en_EN":
+ sys_lang = "en_GB"
+ langs = LANG_TEXT[sys_lang]
+ choices = []
+ for lang in langs:
+ if "_" in lang:
+ choice = (lang[:2], langs[lang])
+ choices.append(choice)
+ logger.debug("choices: %s", choices)
+ return choices
diff --git a/src/List.py b/src/List.py
new file mode 100644
index 0000000..d203c8c
--- /dev/null
+++ b/src/List.py
@@ -0,0 +1,106 @@
+#!/usr/bin/python
+# coding=utf-8
+#
+# Copyright (C) 2018-2024 by dream-alpha
+#
+# In case of reuse of this source code please do not remove this copyright.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# For more information on the GNU General Public License see:
+# .
+
+
+from Components.GUIComponent import GUIComponent
+from enigma import eListboxPythonMultiContent, eListbox, RT_HALIGN_LEFT, RT_VALIGN_CENTER
+from skin import parseFont
+
+
+class List(GUIComponent):
+ GUI_WIDGET = eListbox
+
+ def __init__(self):
+ GUIComponent.__init__(self)
+ self.list = eListboxPythonMultiContent()
+ self.list.setBuildFunc(self.buildList)
+ self.onSelectionChanged = []
+
+ def buildList(self, entry):
+ res = [None]
+ res.append(
+ (
+ eListboxPythonMultiContent.TYPE_TEXT,
+ 5,
+ 0,
+ self.list.getItemSize().width(),
+ self.list.getItemSize().height(),
+ 0,
+ RT_HALIGN_LEFT | RT_VALIGN_CENTER,
+ entry[0]
+ )
+ )
+ return res
+
+ def applySkin(self, desktop, parent):
+ if not self.visible:
+ self.instance.hide()
+
+ if self.skinAttributes is None:
+ return False
+
+ for (attrib, value) in self.skinAttributes:
+ if attrib in ["font"]:
+ self.list.setFont(0, parseFont(value, ((1, 1), (1, 1))))
+ self.skinAttributes.remove((attrib, value))
+
+ GUIComponent.applySkin(self, desktop, parent)
+ return True
+
+ def getCurrent(self):
+ current = self.list.getCurrentSelection()
+ return current and current[0]
+
+ def postWidgetCreate(self, instance):
+ instance.setContent(self.list)
+ self.instance.setWrapAround(True)
+ self.selectionChanged_conn = instance.selectionChanged.connect(self.selectionChanged)
+
+ def preWidgetRemove(self, instance):
+ instance.setContent(None)
+ self.selectionChanged_conn = None
+
+ def selectionChanged(self):
+ for function in self.onSelectionChanged:
+ function()
+
+ def setList(self, alist):
+ self.list.setList(alist)
+
+ def moveToIndex(self, index):
+ self.instance.moveSelectionTo(index)
+
+ def getSelectionIndex(self):
+ return self.list.getCurrentSelectionIndex()
+
+ def selectionEnabled(self, enabled):
+ self.instance.setSelectionEnable(enabled)
+
+ def pageUp(self):
+ self.instance.moveSelection(self.instance.pageUp)
+
+ def pageDown(self):
+ self.instance.moveSelection(self.instance.pageDown)
+
+ def moveUp(self):
+ self.instance.moveSelection(self.instance.moveUp)
+
+ def moveDown(self):
+ self.instance.moveSelection(self.instance.moveDown)
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644
index 0000000..753e261
--- /dev/null
+++ b/src/Makefile.am
@@ -0,0 +1,4 @@
+installdir = $(libdir)/enigma2/python/Plugins/Extensions/TMDBCockpit
+SUBDIRS = skin tmdbsimple
+install_PYTHON = *.py
+install_DATA = *.xml *.png
diff --git a/src/MoreOptions.py b/src/MoreOptions.py
new file mode 100644
index 0000000..4f897ed
--- /dev/null
+++ b/src/MoreOptions.py
@@ -0,0 +1,100 @@
+#!/usr/bin/python
+# coding=utf-8
+#
+# Copyright (C) 2018-2024 by dream-alpha
+#
+# In case of reuse of this source code please do not remove this copyright.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# For more information on the GNU General Public License see:
+# .
+
+import os
+from Screens.ChoiceBox import ChoiceBox
+from Screens.MessageBox import MessageBox
+from .__init__ import _
+from .Debug import logger
+from .Utils import temp_dir
+from .FileUtils import copyFile, writeFile
+
+
+class MoreOptions():
+ def __init__(self, session, service_path):
+ logger.debug("service_path: %s", service_path)
+ self.session = session
+ self.service_path = service_path
+ self.ident = None
+
+ def menu(self, ident, overview):
+ logger.info("ident: %s", ident)
+ self.ident = ident
+ self.overview = overview
+ if self.service_path:
+ options = [
+ (_("Save movie description"), 1),
+ (_("Save movie cover"), 2),
+ (_("Save movie backdrop"), 3),
+ (_("Save movie backdrop as cover"), 4),
+ ("1+2", 5),
+ ("1+2+3", 6),
+ ("2+3", 7)
+ ]
+ self.session.openWithCallback(
+ self.menuCallback,
+ ChoiceBox,
+ windowTitle=_("TMDB cover/backdrop"),
+ title=_("Please select a function"),
+ list=options
+ )
+
+ def menuCallback(self, ret):
+ if ret is not None:
+ msg = ""
+ option = ret[1]
+ ident = str(self.ident)
+ service_filename = os.path.splitext(self.service_path)[0]
+ logger.debug("service_filename: %s", service_filename)
+ msg += "\n" if msg else ""
+ if option in [2, 5, 7]:
+ cover = temp_dir + "cover" + ident + ".jpg"
+ if os.path.isfile(cover):
+ copyFile(cover, service_filename + ".jpg")
+ msg += _("Cover saved.")
+ self.files_saved = True
+ logger.debug("Cover %s.jpg created", service_filename)
+ else:
+ msg += _("No cover available")
+
+ if option in [3, 4, 6, 7]:
+ backdrop = temp_dir + "backdrop" + ident + ".jpg"
+ ext = ".jpg" if option == 4 else ".bdp.jpg"
+ msg += "\n" if msg else ""
+ if os.path.isfile(backdrop):
+ copyFile(backdrop, service_filename + ext)
+ msg += _("Backdrop saved.")
+ self.files_saved = True
+ logger.debug("Backdrop %s%s created", service_filename, ext)
+ else:
+ msg += _("No backdrop available")
+
+ if option in [1, 5, 6]:
+ text_file = service_filename + ".txt"
+ msg += "\n" if msg else ""
+ if self.overview:
+ writeFile(text_file, self.overview)
+ logger.debug("%s created", text_file)
+ msg += _("Movie description saved.")
+ self.files_saved = True
+ else:
+ msg += _("No movie description available")
+
+ self.session.open(MessageBox, msg, type=MessageBox.TYPE_INFO)
diff --git a/src/Parsers.py b/src/Parsers.py
new file mode 100644
index 0000000..c6e5b9c
--- /dev/null
+++ b/src/Parsers.py
@@ -0,0 +1,169 @@
+#!/usr/bin/python
+# coding=utf-8
+#
+# Copyright (C) 2018-2024 by dream-alpha
+#
+# In case of reuse of this source code please do not remove this copyright.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# For more information on the GNU General Public License see:
+# .
+
+
+from .__init__ import _
+from .Json import Json
+
+
+class Parsers(Json):
+ def __init__(self):
+ Json.__init__(self)
+
+ def parseCountry(self, result):
+ country_string = ""
+ for country in result["production_countries"]:
+ result1 = {}
+ self.parseJson(result1, country, ["iso_3166_1"])
+ if country_string:
+ country_string += "/"
+ country_string += result1["iso_3166_1"]
+ result["country"] = country_string
+ result.pop("production_countries", None)
+
+ def parseGenre(self, result):
+ genre_string = ""
+ for genre in result["genres"]:
+ result1 = {}
+ self.parseJson(result1, genre, ["name"])
+ if genre_string:
+ genre_string += ", "
+ genre_string += result1["name"]
+ result["genre"] = genre_string
+
+ def parseCast(self, result):
+ cast_string = ""
+ result1 = result["credits"]
+ for cast in result1["cast"]:
+ result2 = {}
+ self.parseJson(result2, cast, ["name", "character"])
+ cast_string += "%s (%s)\n" % (result2["name"], result2["character"])
+ result["cast"] = cast_string
+
+ def parseCrew(self, result):
+ crew_string = ""
+ director = ""
+ author = ""
+ result1 = result["credits"]
+ for crew in result1["crew"]:
+ result2 = {}
+ self.parseJson(result2, crew, ["name", "job"])
+ crew_string += "%s (%s)\n" % (result2["name"], result2["job"])
+ if result2["job"] == "Director":
+ if director:
+ director += "\n"
+ director += result2["name"]
+ if result2["job"] == "Screenplay" or result2["job"] == "Writer":
+ if author:
+ author += "\n"
+ author += result2["name"]
+ result["crew"] = crew_string
+ result["director"] = director
+ result["author"] = author
+
+ def parseStudio(self, result):
+ studio_string = ""
+ for studio in result["production_companies"]:
+ result1 = {}
+ self.parseJson(result1, studio, ["name"])
+ if studio_string:
+ studio_string += ", "
+ studio_string += result1["name"]
+ result["studio"] = studio_string
+
+ def parseFsk(self, result, media):
+ fsk = ""
+ keys = []
+ if media == "movie":
+ keys = ["countries", "certification"]
+ result1 = result["releases"]
+ elif media == "tv":
+ keys = ["results", "rating"]
+ result1 = result["content_ratings"]
+ if keys:
+ for country in result1[keys[0]]:
+ result2 = {}
+ self.parseJson(result2, country, ["iso_3166_1", keys[1]])
+ if result2["iso_3166_1"] == "DE":
+ fsk = result2[keys[1]].strip("+")
+ result["fsk"] = fsk
+
+ def parseMovieVideos(self, result):
+ result1 = {}
+ self.parseJson(result1, result["videos"], ["results"])
+ result["videos"] = result1["results"]
+ videos = []
+ for video in result["videos"]:
+ result1 = {}
+ self.parseJson(result1, video, ["site"])
+ if result1["site"] == "YouTube":
+ videos.append(video)
+ result["videos"] = videos
+
+ def parseTVCountry(self, result):
+ self.parseJsonList(result, "origin_country", "/")
+ result["country"] = result["origin_country"]
+
+ def parseTVCrew(self, result):
+ director = ""
+ for directors in result["created_by"]:
+ result1 = {}
+ self.parseJson(result1, directors, ["name"])
+ if director:
+ director += "\n"
+ director += result1["name"]
+ result["director"] = _("Various")
+ result["author"] = director
+
+ def parseTVStudio(self, result):
+ studio_string = ""
+ for studio in result["networks"]:
+ result1 = {}
+ self.parseJson(result1, studio, ["name"])
+ if studio_string:
+ studio_string += ", "
+ studio_string += result1["name"]
+ result["studio"] = studio_string
+
+ def parseTVSeasons(self, result):
+ seasons = result["number_of_seasons"]
+ episodes = result["number_of_episodes"]
+ result["runtime"] = "%s %s / %s %s" % (seasons, _("Seasons"), episodes, _("Episodes"))
+
+ seasons_string = ""
+ for seasons in result["seasons"]:
+ result1 = {}
+ self.parseJson(result1, seasons, ["season_number", "episode_count", "air_date"])
+ # logger.debug("seasons: %s", result1)
+ if int(result1["season_number"]) >= 1:
+ seasons_string += "%s %s: %s %s (%s)\n" % (_("Season"), result1["season_number"], result1["episode_count"], _("Episodes"), result1["air_date"][:4])
+ result["seasons"] = seasons_string
+
+ def parsePersonGender(self, result):
+ gender = result["gender"]
+ if gender == 1:
+ gender = _("female")
+ elif gender == 2:
+ gender = _("male")
+ elif gender == "divers":
+ gender = _("divers")
+ else:
+ gender = _("not specified")
+ result["gender"] = gender
diff --git a/src/Picture.py b/src/Picture.py
new file mode 100644
index 0000000..dc691a2
--- /dev/null
+++ b/src/Picture.py
@@ -0,0 +1,55 @@
+#!/usr/bin/python
+# coding=utf-8
+#
+# Copyright (C) 2018-2024 by dream-alpha
+#
+# In case of reuse of this source code please do not remove this copyright.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# For more information on the GNU General Public License see:
+# .
+
+
+import os
+from twisted.internet import threads, reactor
+from Tools.LoadPixmap import LoadPixmap
+from .Debug import logger
+from .Utils import temp_dir
+from .WebRequests import WebRequests
+
+
+class Picture(WebRequests):
+ def __init__(self):
+ WebRequests.__init__(self)
+
+ def showPicture(self, pixmap, atype, ident, url):
+ logger.info("atype: %s, ident: %s, url: %s", atype, ident, url)
+ path = temp_dir + atype + str(ident) + ".jpg"
+ if url and not url.endswith("None") and not os.path.isfile(path):
+ threads.deferToThread(self.downloadPicture, pixmap, url, path, self.displayPicture)
+ else:
+ self.displayPicture(pixmap, path)
+
+ def downloadPicture(self, pixmap, url, path, callback):
+ logger.info("...")
+ self.downloadFile(url, path)
+ reactor.callFromThread(callback, pixmap, path) # pylint: disable=E1101
+
+ def displayPicture(self, pixmap, path):
+ logger.info("...")
+ if pixmap and pixmap.instance:
+ if path and os.path.isfile(path):
+ pixmap.instance.setPixmap(LoadPixmap(path))
+ pixmap.show()
+ else:
+ logger.debug("picture does not exist: %s", path)
+ pixmap.hide()
diff --git a/src/PluginUtils.py b/src/PluginUtils.py
new file mode 100644
index 0000000..a044a53
--- /dev/null
+++ b/src/PluginUtils.py
@@ -0,0 +1,39 @@
+#!/usr/bin/python
+# coding=utf-8
+#
+# Copyright (C) 2018-2024 by dream-alpha
+#
+# In case of reuse of this source code please do not remove this copyright.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# For more information on the GNU General Public License see:
+# .
+
+
+from Components.PluginComponent import plugins
+
+
+WHERE_SEARCH = -99
+WHERE_TMDB_SEARCH = -98
+WHERE_TMDB_MOVIELIST = -97
+WHERE_MEDIATHEK_SEARCH = -96
+WHERE_TVMAGAZINE_SEARCH = -95
+WHERE_COVER_DOWNLOAD = -94
+WHERE_JOBCOCKPIT = -93
+
+
+def getPlugin(where):
+ plugin = None
+ plugins_list = plugins.getPlugins(where=where)
+ if len(plugins_list) > 0:
+ plugin = plugins_list[0]
+ return plugin
diff --git a/src/ScreenConfig.py b/src/ScreenConfig.py
new file mode 100644
index 0000000..f78c3fb
--- /dev/null
+++ b/src/ScreenConfig.py
@@ -0,0 +1,82 @@
+#!/usr/bin/python
+# coding=utf-8
+#
+# Copyright (C) 2018-2024 by dream-alpha
+#
+# In case of reuse of this source code please do not remove this copyright.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# For more information on the GNU General Public License see:
+# .
+
+
+from Components.ActionMap import ActionMap
+from Components.config import config, configfile, getConfigListEntry
+from Components.ConfigList import ConfigListScreen
+from Components.Label import Label
+from Screens.Screen import Screen
+from .__init__ import _
+from .Version import PLUGIN, VERSION
+
+
+class ScreenConfig(Screen, ConfigListScreen):
+ def __init__(self, session):
+ Screen.__init__(self, session)
+ self.skinName = "ScreenConfig"
+
+ self.onChangedEntry = []
+ self.list = []
+ ConfigListScreen.__init__(self, self.list, session=session, on_change=self.changedEntry)
+
+ self["actions"] = ActionMap(
+ ["TMDBActions"],
+ {
+ "cancel": self.exit,
+ "save": self.ok,
+ "red": self.exit,
+ "green": self.ok,
+ },
+ -2
+ )
+
+ self["key_green"] = Label(_("OK"))
+ self["key_red"] = Label(_("Cancel"))
+
+ self.list = []
+ self.createConfigList()
+ self.onLayoutFinish.append(self.__onLayoutFinish)
+
+ def __onLayoutFinish(self):
+ self.setTitle(PLUGIN + " - " + VERSION)
+
+ def createConfigList(self):
+ self.list = []
+ self.list.append(getConfigListEntry(_("Language:"), config.plugins.tmdb.lang))
+ self.list.append(getConfigListEntry(_("Skip to movie details for single result:"), config.plugins.tmdb.skip_to_movie))
+ self.list.append(getConfigListEntry(_("Yellow key for TMDB infos in EPGs:"), config.plugins.tmdb.key_yellow))
+ self.list.append(getConfigListEntry(_("Cover resolution:"), config.plugins.tmdb.cover_size))
+ self.list.append(getConfigListEntry(_("Backdrop resolution:"), config.plugins.tmdb.backdrop_size))
+ self.list.append(getConfigListEntry(_("Player for trailers:"), config.plugins.tmdb.trailer_player))
+ self["config"].setList(self.list)
+
+ def changedEntry(self):
+ for x in self.onChangedEntry:
+ x()
+
+ def ok(self):
+ for x in self["config"].list:
+ x[1].save()
+ configfile.save()
+ self.close()
+
+ def exit(self):
+ self.close()
diff --git a/src/ScreenMain.py b/src/ScreenMain.py
new file mode 100644
index 0000000..31c13e1
--- /dev/null
+++ b/src/ScreenMain.py
@@ -0,0 +1,327 @@
+#!/usr/bin/python
+# coding=utf-8
+#
+# Copyright (C) 2018-2024 by dream-alpha
+#
+# In case of reuse of this source code please do not remove this copyright.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# For more information on the GNU General Public License see:
+# .
+
+import os
+from twisted.internet import threads, reactor
+from Components.ActionMap import HelpableActionMap
+from Components.Label import Label
+from Components.Pixmap import Pixmap
+from Components.config import config
+from enigma import eServiceCenter
+from Screens.VirtualKeyBoard import VirtualKeyBoard
+from Screens.HelpMenu import HelpableScreen
+from Screens.ChoiceBox import ChoiceBox
+from Screens.Screen import Screen
+from . import tmdbsimple as tmdb
+from .__init__ import _
+from .List import List
+from .ScreenConfig import ScreenConfig
+from .ScreenMovie import ScreenMovie
+from .ScreenPerson import ScreenPerson
+from .Utils import temp_dir, cleanText
+from .Picture import Picture
+from .FileUtils import createDirectory, deleteDirectory
+from .Debug import logger
+from .DelayTimer import DelayTimer
+from .Json import Json
+from .SearchMain import SearchMain
+from .Utils import getApiKey
+
+
+class ScreenMain(Picture, Json, Screen, HelpableScreen):
+ def __init__(self, session, service, mode):
+ Screen.__init__(self, session)
+ Picture.__init__(self)
+ Json.__init__(self)
+ self.session = session
+
+ tmdb.API_KEY = getApiKey()
+
+ self.title = "TMDB - The Movie Database - " + _("Overview")
+ self.menu_selection = 0
+ self.search_title = ""
+ self.service_title = ""
+ self.direction_up = False
+ self.page = 1
+ self.total_pages = 0
+ self.ident = 0
+ self.count = 0
+ self.service_path = ""
+ self.files_saved = False
+ self.result = []
+
+ self['searchinfo'] = Label()
+ self['key_red'] = Label(_("Cancel"))
+ self['key_green'] = Label(_("Details"))
+ self['key_yellow'] = Label(_("Edit search"))
+ self['key_blue'] = Label(_("more ..."))
+ self['list'] = List()
+ self['cover'] = Pixmap()
+ self['backdrop'] = Pixmap()
+
+ HelpableScreen.__init__(self)
+ self["actions"] = HelpableActionMap(
+ self,
+ "TMDBActions",
+ {
+ "ok": (self.ok, _("Show details")),
+ "cancel": (self.exit, _("Exit")),
+ "down": (self.moveDown, _("Up")),
+ "up": (self.moveUp, _("Down")),
+ "nextBouquet": (self.nextBouquet, _("Details down")),
+ "prevBouquet": (self.prevBouquet, _("Details up")),
+ "red": (self.exit, _("Cancel")),
+ "green": (self.ok, _("Show details")),
+ "yellow": (self.searchString, _("Edit search")),
+ "blue": (self.menu, _("more ...")),
+ "menu": (self.setup, _("Setup")),
+ "eventview": (self.searchString, _("Edit search"))
+ },
+ -1,
+ )
+
+ if mode == 1:
+ self.service_path = service.getPath()
+ self.service_name = service.getName()
+ if self.service_name:
+ self.text = cleanText(self.service_name)
+ else:
+ if os.path.isdir(self.service_path):
+ self.service_path = os.path.normpath(self.service_path)
+ self.text = cleanText(os.path.basename(self.service_path))
+ else:
+ info = eServiceCenter.getInstance().info(service)
+ name = info.getName(service)
+ self.text = cleanText(os.path.splitext(name)[0])
+ elif mode == 2:
+ name = service
+ self.text = cleanText(name)
+ else:
+ self.text = ""
+ self.menu_selection = 1
+ self.search_title = _("Current movies in cinemas")
+
+ logger.debug("text: %s", self.text)
+
+ createDirectory(temp_dir)
+ self.onLayoutFinish.append(self.onDialogShow)
+ self["list"].onSelectionChanged.append(self.onSelectionChanged)
+
+ def onSelectionChanged(self):
+ DelayTimer.stopAll()
+ self.displayPicture(self["cover"], None)
+ self.displayPicture(self["backdrop"], None)
+ if config.plugins.tmdb.skip_to_movie.value and self.count == 1:
+ DelayTimer(10, self.ok)
+ else:
+ DelayTimer(100, self.showPictures)
+
+ def onDialogShow(self):
+ logger.info("...")
+ if self.menu_selection or self.text:
+ self.searchData()
+ else:
+ logger.debug("no search string specified")
+ self["searchinfo"].setText(_("No search string specified."))
+
+ def searchData(self):
+ logger.debug("menu_selection: %s, text: %s", self.menu_selection, self.text)
+ self.result = []
+ if self.menu_selection:
+ self["searchinfo"].setText(self.search_title)
+ threads.deferToThread(self.getData, self.menu_selection, self.text, self.ident, self.page, self.gotData)
+ else:
+ self.search_iteration = 0
+ self.search_words = self.text.split(" ")
+ self.last_text = self.text
+ self["searchinfo"].setText(_("Looking up: %s ...") % self.text)
+ self.search(0, [])
+
+ def search(self, totalpages, result):
+ if self.search_iteration and self.search_words:
+ del self.search_words[-1]
+ text = " ".join(self.search_words)
+ else:
+ text = self.text
+ if not result and text:
+ self["searchinfo"].setText(_("Looking up: %s ...") % text)
+ self.search_iteration += 1
+ self.last_text = text
+ logger.debug("iteration: %s, text: %s", self.search_iteration, text)
+ threads.deferToThread(self.getData, self.menu_selection, text, self.ident, self.page, self.search)
+ else:
+ self.gotData(totalpages, result, self.last_text)
+
+ def getData(self, menu_selection, text, ident, page, callback):
+ totalpages, result = SearchMain().getResult(self.result, menu_selection, text, ident, page)
+ reactor.callFromThread(callback, totalpages, result) # pylint: disable=E1101
+
+ def gotData(self, totalpages, result, text=""):
+ logger.info("text: %s", text)
+ logger.info("len(result): %s", len(result))
+ self.count = len(result)
+ self.totalpages = totalpages
+ if self.menu_selection:
+ if result:
+ self["searchinfo"].setText("%s (%s %s/%s)" % (self.search_title, _("page"), self.page, totalpages))
+ else:
+ self['searchinfo'].setText(_("No results for: %s") % self.search_title)
+ else:
+ if result:
+ if text != self.text:
+ self['searchinfo'].setText("%s (%s)" % (text, self.text))
+ else:
+ self['searchinfo'].setText(self.text)
+ else:
+ self['searchinfo'].setText(_("No results for: %s") % self.text)
+ self["list"].setList(result)
+ if result:
+ if self.direction_up:
+ self["list"].moveToIndex(len(self.result) - 1)
+ else:
+ self["list"].moveToIndex(0)
+
+ def showPictures(self):
+ current = self["list"].getCurrent()
+ if current:
+ ident = current[1]
+ cover_url = current[3]
+ backdrop_url = current[4]
+ self.showPicture(self["cover"], "cover", ident, cover_url)
+ self.showPicture(self["backdrop"], "backdrop", ident, backdrop_url)
+ else:
+ self.showPicture(self["cover"], "cover", "", None)
+ self.showPicture(self["backdrop"], "backdrop", "", None)
+
+ def ok(self):
+ current = self['list'].getCurrent()
+ logger.info("current: %s", current)
+ if current:
+ title = current[0]
+ ident = current[1]
+ media = current[2]
+ cover_url = current[3]
+ backdrop_url = current[4]
+ if media in ["movie", "tv"]:
+ self.session.openWithCallback(self.screenMovieCallback, ScreenMovie, title, media, cover_url, ident, self.service_path, backdrop_url)
+ elif media == "person":
+ self.session.openWithCallback(self.screenPersonCallback, ScreenPerson, title, ident, "")
+ else:
+ logger.debug("unsupported media: %s", media)
+
+ def screenMovieCallback(self, do_exit, files_saved):
+ logger.info("files_saved: %s", files_saved)
+ self.files_saved = files_saved
+ if do_exit:
+ self.exit()
+ elif self.count == 1:
+ self.showPictures()
+
+ def screenPersonCallback(self, do_exit):
+ if do_exit:
+ self.exit()
+
+ def menu(self):
+ logger.info("...")
+ self.direction_up = False
+ options = [
+ (_("TMDB Infos"), 0),
+ (_("Current movies in cinemas"), 1),
+ (_("Upcoming movies"), 2),
+ (_("Popular movies"), 3),
+ (_("Similar movies"), 4),
+ (_("Recommendations"), 5),
+ (_("Best rated movies"), 6)
+ ]
+ self.session.openWithCallback(
+ self.menuCallback,
+ ChoiceBox,
+ windowTitle=_("TMDB categories"),
+ title=_("Please select a category"),
+ list=options
+ )
+
+ def menuCallback(self, ret):
+ logger.info("ret: %s", ret)
+ if ret is not None:
+ self.page = 1
+ self.search_title = ret[0]
+ self.menu_selection = ret[1]
+ current = self['list'].getCurrent()
+ if current:
+ self.service_title = current[0]
+ self.ident = current[1]
+ self.searchData()
+
+ def moveUp(self):
+ logger.info("SelectionIndex: %s, len(self.result): %s", self["list"].getSelectionIndex(), len(self.result))
+ if self["list"].getSelectionIndex() == 0:
+ self.nextBouquet()
+ else:
+ self["list"].moveUp()
+
+ def moveDown(self):
+ logger.info("SelectionIndex: %s, len(self.result): %s", self["list"].getSelectionIndex(), len(self.result))
+ if self["list"].getSelectionIndex() == len(self.result) - 1:
+ self.prevBouquet()
+ else:
+ self["list"].moveDown()
+
+ def prevBouquet(self):
+ logger.info("...")
+ if self.menu_selection:
+ self.direction_up = False
+ self.page += 1
+ if self.page > self.totalpages:
+ self.page = 1
+ self.searchData()
+
+ def nextBouquet(self):
+ logger.info("...")
+ if self.menu_selection:
+ self.direction_up = True
+ self.page -= 1
+ if self.page <= 0:
+ self.page = self.totalpages
+ self.searchData()
+
+ def setup(self):
+ self.session.open(ScreenConfig)
+
+ def searchString(self):
+ self.menu_selection = 0
+ current = self['list'].getCurrent()
+ logger.info("current: %s", current)
+ if current:
+ search_title = current[5]
+ self.text = search_title
+ self.session.openWithCallback(self.goSearch, VirtualKeyBoard, title=(_("Search for Movie:")), text=self.text)
+
+ def goSearch(self, text):
+ if text:
+ self.text = text
+ self.searchData()
+
+ def exit(self):
+ logger.info("files_saved: %s", self.files_saved)
+ DelayTimer.stopAll()
+ self["list"].onSelectionChanged.remove(self.onSelectionChanged)
+ deleteDirectory(temp_dir)
+ self.close(self.files_saved)
diff --git a/src/ScreenMovie.py b/src/ScreenMovie.py
new file mode 100644
index 0000000..bea6b34
--- /dev/null
+++ b/src/ScreenMovie.py
@@ -0,0 +1,237 @@
+#!/usr/bin/python
+# coding=utf-8
+#
+# Copyright (C) 2018-2024 by dream-alpha
+#
+# In case of reuse of this source code please do not remove this copyright.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# For more information on the GNU General Public License see:
+# .
+
+
+from twisted.internet import threads, reactor
+from enigma import eServiceReference
+from Components.config import config
+from Components.ActionMap import HelpableActionMap
+from Components.Label import Label
+from Components.Pixmap import Pixmap
+from Components.ScrollLabel import ScrollLabel
+from Screens.ChoiceBox import ChoiceBox
+from Screens.MoviePlayer import MoviePlayer
+from Screens.HelpMenu import HelpableScreen
+from Screens.Screen import Screen
+from Screens.MessageBox import MessageBox
+from Tools.LoadPixmap import LoadPixmap
+from Tools.BoundFunction import boundFunction
+from .__init__ import _
+from .ScreenConfig import ScreenConfig
+from .ScreenPeople import ScreenPeople
+from .ScreenSeason import ScreenSeason
+from .Picture import Picture
+from .Debug import logger
+from .SearchMovie import SearchMovie
+from .MoreOptions import MoreOptions
+from .PluginUtils import getPlugin, WHERE_SEARCH
+
+
+class ScreenMovie(MoreOptions, Picture, Screen, HelpableScreen):
+ def __init__(self, session, movie, media, cover_url, ident, service_path, backdrop_url):
+ logger.debug(
+ "movie: %s, media: %s, cover_url: %s, ident: %s, service_path: %s, backdrop_url: %s",
+ movie, media, cover_url, ident, service_path, backdrop_url
+ )
+ Screen.__init__(self, session)
+ Picture.__init__(self)
+ MoreOptions.__init__(self, session, service_path)
+ self.title = "TMDB - The Movie Database - " + _("Movie Details")
+ self.session = session
+ self.movie = movie
+ self.media = media
+ self.cover_url = cover_url
+ self.backdrop_url = backdrop_url
+ self.ident = ident
+ self.service_path = service_path
+ self.files_saved = False
+ self.overview = ""
+ self.result = {}
+
+ self["genre"] = Label()
+ self["genre_txt"] = Label()
+ self["fulldescription"] = self.fulldescription = ScrollLabel("")
+ self["rating"] = Label()
+ self["votes"] = Label()
+ self["votes_brackets"] = Label()
+ self["votes_txt"] = Label()
+ self["runtime"] = Label()
+ self["runtime_txt"] = Label()
+ self["year"] = Label()
+ self["year_txt"] = Label()
+ self["country"] = Label()
+ self["country_txt"] = Label()
+ self["director"] = Label()
+ self["director_txt"] = Label()
+ self["author"] = Label()
+ self["author_txt"] = Label()
+ self["studio"] = Label()
+ self["studio_txt"] = Label()
+
+ self.fields = {
+ "genre": (_("Genre:"), "-"),
+ "fulldescription": (None, ""),
+ "rating": (None, "0.0"),
+ "votes": (_("Votes:"), "-"),
+ "votes_brackets": (None, ""),
+ "runtime": (_("Runtime:"), "-"),
+ "year": (_("Year:"), "-"),
+ "country": (_("Countries:"), "-"),
+ "director": (_("Director:"), "-"),
+ "author": (_("Author:"), "-"),
+ "studio": (_("Studio:"), "-"),
+ }
+
+ self["key_red"] = Label(_("Cancel"))
+ self["key_green"] = Label(_("Crew"))
+ self["key_yellow"] = Label(_("Seasons")) if self.media == "tv" else Label("")
+ self["key_blue"] = Label(_("more ...")) if self.service_path else Label("")
+
+ self["searchinfo"] = Label()
+ self["cover"] = Pixmap()
+ self["backdrop"] = Pixmap()
+ self["fsklogo"] = Pixmap()
+ self["star"] = Pixmap()
+
+ HelpableScreen.__init__(self)
+ self["actions"] = HelpableActionMap(
+ self,
+ "TMDBActions",
+ {
+ "ok": (self.green, _("Crew")),
+ "cancel": (boundFunction(self.exit, True), _("Exit")),
+ "up": (self.fulldescription.pageUp, _("Selection up")),
+ "down": (self.fulldescription.pageDown, _("Selection down")),
+ "left": (self.fulldescription.pageUp, _("Page up")),
+ "right": (self.fulldescription.pageDown, _("Page down")),
+ "red": (boundFunction(self.exit, False), _("Cancel")),
+ "green": (self.green, _("Crew")),
+ "yellow": (self.yellow, _("Seasons")),
+ "blue": (self.showMenu, _("more ...")),
+ "menu": (self.setup, _("Setup")),
+ "eventview": (self.search, _("Search"))
+ },
+ -1,
+ )
+
+ self.onLayoutFinish.append(self.__onLayoutFinish)
+
+ def __onLayoutFinish(self):
+ logger.debug("movie: %s", self.movie)
+ self.showPicture(self["cover"], "cover", self.ident, self.cover_url)
+ self.showPicture(self["backdrop"], "backdrop", self.ident, self.backdrop_url)
+ self["searchinfo"].setText(_("Looking up: %s ...") % self.movie)
+ threads.deferToThread(self.getData, self.gotData)
+
+ def getData(self, callback):
+ result = SearchMovie().getResult(self.result, self.ident, self.media)
+ logger.debug("result: %s", result)
+ reactor.callFromThread(callback, result) # pylint: disable=E1101
+
+ def gotData(self, result):
+ if not result:
+ self["searchinfo"].setText(_("No results for: %s") % self.movie)
+ self.overview = ""
+ else:
+ self["searchinfo"].setText(self.movie)
+ path = "/usr/lib/enigma2/python/Plugins/Extensions/TMDBCockpit/skin/images/star.png"
+ self["star"].instance.setPixmap(LoadPixmap(path))
+ path = "/usr/lib/enigma2/python/Plugins/Extensions/TMDBCockpit/skin/images/fsk_" + result["fsk"] + ".png"
+ self["fsklogo"].instance.setPixmap(LoadPixmap(path))
+
+ for field in self.fields:
+ # logger.debug("field: %s", field)
+ # logger.debug("result: %s", result[field])
+ if self.fields[field][0]:
+ self[field + "_txt"].setText(self.fields[field][0])
+ if result[field]:
+ self[field].setText(result[field])
+ else:
+ self[field].setText(self.fields[field][1])
+
+ self.overview = result["overview"]
+
+ self.movie_title = self.original_title = ""
+ if self.media == "movie":
+ self.movie_title = result["title"]
+ self.original_title = result["original_title"]
+ self.videos = result["videos"]
+ self["key_yellow"].setText(_("Videos") + " (%s)" % len(self.videos))
+ elif self.media == "tv":
+ self.movie_title = result["name"]
+
+ def showMenu(self):
+ self.menu(self.ident, self.overview)
+
+ def search(self):
+ search_plugin = getPlugin(WHERE_SEARCH)
+ if search_plugin:
+ search_plugin(self.session, self.movie_title, self.original_title)
+ else:
+ self.session.open(MessageBox, _("No search provider registered."), type=MessageBox.TYPE_INFO)
+
+ def setup(self):
+ self.session.open(ScreenConfig)
+
+ def yellow(self):
+ if self.media == "tv":
+ self.session.openWithCallback(self.screenSeasonCallback, ScreenSeason, self.movie, self.ident, self.media, self.service_path)
+ elif self.media == "movie" and self.videos:
+ videolist = []
+ for video in self.videos:
+ vKey = video["key"]
+ vName = video["name"]
+ if config.plugins.tmdb.trailer_player.value == "MyTube":
+ vLink = "8193:0:1:0:0:0:0:0:0:0:yt%3a//"
+ else:
+ vLink = "8193:0:1:0:0:0:0:0:0:0:mp_yt%3a//"
+ videolist.append((str(vName), str(vLink + "%s:%s" % (vKey, vName))))
+
+ if len(videolist) > 1:
+ videolist = sorted(videolist, key=lambda x: x[0])
+ self.session.openWithCallback(
+ self.videolistCallback,
+ ChoiceBox,
+ windowTitle=_("TMDB videos"),
+ title=_("Please select a video"),
+ list=videolist,
+ )
+ elif len(videolist) == 1:
+ self.videolistCallback(videolist[0])
+
+ def screenSeasonCallback(self, do_exit, files_saved):
+ self.files_saved = files_saved
+ if do_exit:
+ self.exit(True)
+
+ def videolistCallback(self, ret):
+ ret = ret and ret[1]
+ if ret:
+ self.session.open(MoviePlayer, eServiceReference(ret), streamMode=True, askBeforeLeaving=False)
+
+ def green(self):
+ self.session.openWithCallback(self.screenPeopleCallback, ScreenPeople, self.movie, self.ident , self.media, self.cover_url, self.backdrop_url)
+
+ def screenPeopleCallback(self, do_exit):
+ if do_exit:
+ self.exit(True)
+
+ def exit(self, do_exit):
+ self.close(do_exit, self.files_saved)
diff --git a/src/ScreenPeople.py b/src/ScreenPeople.py
new file mode 100644
index 0000000..2c34e25
--- /dev/null
+++ b/src/ScreenPeople.py
@@ -0,0 +1,130 @@
+#!/usr/bin/python
+# coding=utf-8
+#
+# Copyright (C) 2018-2024 by dream-alpha
+#
+# In case of reuse of this source code please do not remove this copyright.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# For more information on the GNU General Public License see:
+# .
+
+from twisted.internet import threads, reactor
+from Components.ActionMap import HelpableActionMap
+from Components.Label import Label
+from Components.Pixmap import Pixmap
+from Screens.HelpMenu import HelpableScreen
+from Screens.Screen import Screen
+from Tools.BoundFunction import boundFunction
+from .__init__ import _
+from .List import List
+from .ScreenConfig import ScreenConfig
+from .ScreenPerson import ScreenPerson
+from .Picture import Picture
+from .Debug import logger
+from .DelayTimer import DelayTimer
+from .SearchPeople import SearchPeople
+
+
+class ScreenPeople(Picture, Screen, HelpableScreen):
+ def __init__(self, session, movie, ident, media, cover_url, backdrop_url):
+ logger.info("movie %s, ident: %s, media: %s, cover_url: %s, backdrop_url: %s", movie, ident, media, cover_url, backdrop_url)
+ Screen.__init__(self, session)
+ Picture.__init__(self)
+ self.title = "TMDB - The Movie Database - " + _("Crew")
+ self.session = session
+ self.movie = movie
+ self.ident = ident
+ self.media = media
+ self.cover_url = cover_url
+ self.backdrop_url = backdrop_url
+ self.result = []
+
+ self['searchinfo'] = Label()
+ self['key_red'] = Label(_("Cancel"))
+ self['key_green'] = Label(_("Details"))
+ self["key_yellow"] = Label()
+ self['key_blue'] = Label()
+ self['list'] = self.list = List()
+ self['cover'] = Pixmap()
+ self['backdrop'] = Pixmap()
+
+ HelpableScreen.__init__(self)
+ self["actions"] = HelpableActionMap(
+ self,
+ "TMDBActions",
+ {
+ "ok": (self.ok, _("Show details")),
+ "cancel": (boundFunction(self.exit, True), _("Exit")),
+ "up": (self.list.moveUp, _("Selection up")),
+ "down": (self.list.moveDown, _("Selection down")),
+ "right": (self.list.pageDown, _("Page down")),
+ "left": (self.list.pageUp, _("Page down")),
+ "red": (boundFunction(self.exit, False), _("Cancel")),
+ "green": (self.ok, _("Show details")),
+ "menu": (self.setup, _("Setup"))
+ },
+ -1,
+ )
+
+ self.onLayoutFinish.append(self.__onLayoutFinish)
+ self["list"].onSelectionChanged.append(self.onSelectionChanged)
+
+ def onSelectionChanged(self):
+ DelayTimer.stopAll()
+ self.displayPicture(self["cover"], None)
+ if self["list"].getCurrent():
+ DelayTimer(200, self.showInfo)
+
+ def __onLayoutFinish(self):
+ logger.debug("movie: %s", self.movie)
+ self.showPicture(self["backdrop"], "backdrop", self.ident, self.backdrop_url)
+ threads.deferToThread(self.getData, self.gotData)
+
+ def getData(self, callback):
+ self["searchinfo"].setText(_("Looking up: %s ...") % (self.movie + " - " + _("Crew")))
+ result = SearchPeople().getResult(self.result, self.ident, self.media)
+ reactor.callFromThread(callback, result) # pylint: disable=E1101
+
+ def gotData(self, result):
+ if not result:
+ self["searchinfo"].setText(_("No results for: %s") % _("Crew"))
+ else:
+ self["searchinfo"].setText(self.movie + " - " + _("Crew"))
+ self["list"].setList(result)
+
+ def showInfo(self):
+ current = self["list"].getCurrent()
+ if current:
+ cover_url = current[2]
+ cover_ident = current[3]
+ self.showPicture(self["cover"], "cover", cover_ident, cover_url)
+
+ def ok(self):
+ current = self['list'].getCurrent()
+ if current:
+ name = current[1]
+ cover_ident = current[3]
+ if cover_ident:
+ self.session.openWithCallback(self.screenPersonCallback, ScreenPerson, name, cover_ident, self.ident)
+
+ def screenPersonCallback(self, do_exit):
+ if do_exit:
+ self.exit(True)
+
+ def setup(self):
+ self.session.open(ScreenConfig)
+
+ def exit(self, do_exit):
+ DelayTimer.stopAll()
+ self["list"].onSelectionChanged.remove(self.onSelectionChanged)
+ self.close(do_exit)
diff --git a/src/ScreenPerson.py b/src/ScreenPerson.py
new file mode 100644
index 0000000..f8dc16f
--- /dev/null
+++ b/src/ScreenPerson.py
@@ -0,0 +1,112 @@
+#!/usr/bin/python
+# coding=utf-8
+#
+# Copyright (C) 2018-2024 by dream-alpha
+#
+# In case of reuse of this source code please do not remove this copyright.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# For more information on the GNU General Public License see:
+# .
+
+
+from twisted.internet import threads, reactor
+from Components.ActionMap import HelpableActionMap
+from Components.Label import Label
+from Components.Pixmap import Pixmap
+from Components.ScrollLabel import ScrollLabel
+from Screens.HelpMenu import HelpableScreen
+from Screens.Screen import Screen
+from Tools.BoundFunction import boundFunction
+from .__init__ import _
+from .Picture import Picture
+from .Debug import logger
+from .ScreenConfig import ScreenConfig
+from .SearchPerson import SearchPerson
+
+
+class ScreenPerson(Picture, Screen, HelpableScreen):
+ def __init__(self, session, person, cover_ident, backdrop_ident):
+ logger.info("cover_ident: %s, backdrop_ident: %s", cover_ident, backdrop_ident)
+ Screen.__init__(self, session)
+ Picture.__init__(self)
+ self.title = "TMDB - The Movie Database - " + _("Person Details")
+ self.session = session
+ self.person = person
+ self.cover_ident = cover_ident
+ self.backdrop_ident = backdrop_ident
+ self.result = {}
+
+ self['searchinfo'] = Label()
+ self['fulldescription'] = self.fulldescription = ScrollLabel("")
+ self['cover'] = Pixmap()
+ self['backdrop'] = Pixmap()
+
+ self['key_red'] = Label(_("Cancel"))
+ self['key_green'] = Label()
+ self['key_yellow'] = Label()
+ self['key_blue'] = Label()
+
+ HelpableScreen.__init__(self)
+ self["actions"] = HelpableActionMap(
+ self,
+ "TMDBActions",
+ {
+ "cancel": (boundFunction(self.exit, True), _("Exit")),
+ "up": (self.fulldescription.pageUp, _("Selection up")),
+ "down": (self.fulldescription.pageDown, _("Selection down")),
+ "left": (self.fulldescription.pageUp, _("Page up")),
+ "right": (self.fulldescription.pageDown, _("Page down")),
+ "red": (boundFunction(self.exit, False), _("Cancel")),
+ "menu": (self.setup, _("Setup")),
+ },
+ -1,
+ )
+
+ self.onLayoutFinish.append(self.__onLayoutFinish)
+
+ def __onLayoutFinish(self):
+ self.showPicture(self["backdrop"], "backdrop", self.backdrop_ident, None)
+ self.showPicture(self["cover"], "cover", self.cover_ident, None)
+ self["searchinfo"].setText(_("Looking up: %s ...") % self.person)
+ threads.deferToThread(self.getData, self.gotData)
+
+ def getData(self, callback):
+ result = SearchPerson().getResult(self.result, self.cover_ident)
+ logger.debug("result: %s", result)
+ reactor.callFromThread(callback, result) # pylint: disable=E1101
+
+ def gotData(self, result):
+ if not result:
+ self["searchinfo"].setText(_("No results for: %s") % self.person)
+ else:
+ self["searchinfo"].setText(result["name"])
+ if result["birthday"] == "None":
+ result["birthday"] = _("not specified")
+ if result["place_of_birth"] == "None":
+ result["place_of_birth"] = _("not specified")
+ fulldescription = result["birthday"] + ", " \
+ + result["place_of_birth"] + ", " \
+ + result["gender"] + "\n" \
+ + result["also_known_as"] + "\n" \
+ + _("Popularity") + ": " + result["popularity"] + "\n\n" \
+ + result["biography"] + "\n\n"
+ if result["movies"]:
+ fulldescription += _("Known for:") + "\n" \
+ + result["movies"]
+ self["fulldescription"].setText(fulldescription)
+
+ def setup(self):
+ self.session.open(ScreenConfig)
+
+ def exit(self, do_exit):
+ self.close(do_exit)
diff --git a/src/ScreenSeason.py b/src/ScreenSeason.py
new file mode 100644
index 0000000..1f74f70
--- /dev/null
+++ b/src/ScreenSeason.py
@@ -0,0 +1,127 @@
+#!/usr/bin/python
+# coding=utf-8
+#
+# Copyright (C) 2018-2024 by dream-alpha
+#
+# In case of reuse of this source code please do not remove this copyright.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# For more information on the GNU General Public License see:
+# .
+
+from twisted.internet import threads, reactor
+from Components.ActionMap import HelpableActionMap
+from Components.Label import Label
+from Components.Pixmap import Pixmap
+from Components.ScrollLabel import ScrollLabel
+from Screens.HelpMenu import HelpableScreen
+from Screens.Screen import Screen
+from Tools.BoundFunction import boundFunction
+from .__init__ import _
+from .List import List
+from .ScreenConfig import ScreenConfig
+from .Picture import Picture
+from .Debug import logger
+from .DelayTimer import DelayTimer
+from .SearchSeason import SearchSeason
+from .MoreOptions import MoreOptions
+
+
+class ScreenSeason(MoreOptions, Picture, Screen, HelpableScreen):
+ def __init__(self, session, movie, ident, media, service_path):
+ logger.info("movie: %s, ident: %s, media: %s", movie, ident, media)
+ Screen.__init__(self, session)
+ MoreOptions.__init__(self, session, service_path)
+ self.title = "TMDB - The Movie Database - " + _("Seasons")
+ Picture.__init__(self)
+ self.session = session
+ self.movie = movie
+ self.ident = ident
+ self.media = media
+ self.service_path = service_path
+ self.files_saved = False
+ self.result = []
+ self['searchinfo'] = Label()
+ self["overview"] = self.overview_label = ScrollLabel()
+ self['key_red'] = Label(_("Cancel"))
+ self['key_green'] = Label()
+ self['key_yellow'] = Label()
+ self["key_blue"] = Label(_("more ...")) if self.service_path else Label("")
+ self['list'] = self.list = List()
+ self['cover'] = Pixmap()
+ self['backdrop'] = Pixmap()
+
+ HelpableScreen.__init__(self)
+ self["actions"] = HelpableActionMap(
+ self,
+ "TMDBActions",
+ {
+ "cancel": (boundFunction(self.exit, True), _("Exit")),
+ "up": (self.list.moveUp, _("Selection up")),
+ "down": (self.list.moveDown, _("Selection down")),
+ "nextBouquet": (self.overview_label.pageUp, _("Details down")),
+ "prevBouquet": (self.overview_label.pageDown, _("Details up")),
+ "right": (self.list.pageDown, _("Page down")),
+ "left": (self.list.pageUp, _("Page down")),
+ "red": (boundFunction(self.exit, False), _("Cancel")),
+ "blue": (self.showMenu, _("more ...")),
+ "menu": (self.setup, _("Setup"))
+ },
+ -1,
+ )
+
+ self.onLayoutFinish.append(self.onFinish)
+ self["list"].onSelectionChanged.append(self.onSelectionChanged)
+
+ def onSelectionChanged(self):
+ DelayTimer.stopAll()
+ if self["list"].getCurrent():
+ DelayTimer(200, self.showInfo)
+
+ def onFinish(self):
+ logger.debug("Selected: %s", self.movie)
+ self.showPicture(self["backdrop"], "backdrop", self.ident, None)
+ threads.deferToThread(self.getData, self.gotData)
+
+ def getData(self, callback):
+ self["searchinfo"].setText(_("Looking up: %s ...") % (self.movie + " - " + _("Seasons")))
+ result = SearchSeason().getResult(self.result, self.ident)
+ reactor.callFromThread(callback, result) # pylint: disable=E1101
+
+ def gotData(self, result):
+ if not result:
+ self["searchinfo"].setText(_("No results for: %s") % _("Seasons"))
+ else:
+ self["searchinfo"].setText(self.movie + " - " + _("Seasons"))
+ self["list"].setList(result)
+
+ def showMenu(self):
+ self.menu(self.ident, self.overview)
+
+ def showInfo(self):
+ self["overview"].setText("...")
+ current = self['list'].getCurrent()
+ if current:
+ cover_url = current[1]
+ self.overview = current[2]
+ self.ident = current[3]
+ logger.debug("ident: %s", self.ident)
+ self.showPicture(self["cover"], "cover", self.ident, cover_url)
+ self["overview"].setText(self.overview)
+
+ def setup(self):
+ self.session.open(ScreenConfig)
+
+ def exit(self, do_exit):
+ DelayTimer.stopAll()
+ self["list"].onSelectionChanged.remove(self.onSelectionChanged)
+ self.close(do_exit, self.files_saved)
diff --git a/src/ScreenTMDB.py b/src/ScreenTMDB.py
new file mode 100644
index 0000000..ff861bf
--- /dev/null
+++ b/src/ScreenTMDB.py
@@ -0,0 +1,75 @@
+#!/usr/bin/python
+# coding=utf-8
+#
+# Copyright (C) 2018-2024 by dream-alpha
+#
+# In case of reuse of this source code please do not remove this copyright.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# For more information on the GNU General Public License see:
+# .
+
+from twisted.internet import threads, reactor
+from . import tmdbsimple as tmdb
+from .Utils import cleanText
+from .Debug import logger
+from .SearchTMDB import SearchTMDB
+from .SearchMovie import SearchMovie
+from .Utils import getApiKey
+
+
+class ScreenTMDB():
+ def __init__(self, text, callback):
+ tmdb.API_KEY = getApiKey()
+
+ self.result1 = []
+ self.result2 = {}
+ self.callback = callback
+ self.text = cleanText(text)
+ logger.debug("text: %s", self.text)
+ self.search_iteration = 0
+ self.search_words = self.text.split(" ")
+ if self.text:
+ self.search([])
+ else:
+ self.callback({})
+
+ def search(self, result):
+ if self.search_iteration and self.search_words:
+ del self.search_words[-1]
+ text = " ".join(self.search_words)
+ else:
+ text = self.text
+ if not result and text:
+ self.search_iteration += 1
+ logger.debug("iteration: %s, text: >%s<", self.search_iteration, text)
+ threads.deferToThread(self.getData, text, self.search)
+ else:
+ self.gotData(result)
+
+ def getData(self, text, callback):
+ result = SearchTMDB().getResult(self.result1, text)
+ reactor.callFromThread(callback, result) # pylint: disable=E1101
+
+ def gotData(self, result):
+ logger.info("result: %s", result)
+ if result:
+ current = result[0][0]
+ logger.debug("current: %s", current)
+ ident = current[1]
+ media = current[2]
+ cover_url = current[3]
+ if media in ["movie", "tv"]:
+ result = SearchMovie().getResult(self.result2, ident, media)
+ result["cover_url"] = cover_url
+ logger.debug("result: %s", result)
+ self.callback(result)
diff --git a/src/SearchMain.py b/src/SearchMain.py
new file mode 100644
index 0000000..b552812
--- /dev/null
+++ b/src/SearchMain.py
@@ -0,0 +1,100 @@
+#!/usr/bin/python
+# coding=utf-8
+#
+# Copyright (C) 2018-2024 by dream-alpha
+#
+# In case of reuse of this source code please do not remove this copyright.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# For more information on the GNU General Public License see:
+# .
+
+from Components.config import config
+from . import tmdbsimple as tmdb
+from .__init__ import _
+from .Debug import logger
+from .Json import Json
+
+
+class SearchMain(Json):
+
+ def __init__(self):
+ Json.__init__(self)
+
+ def getResult(self, res, menu_selection, text, ident, page):
+ logger.info("menu_selection: %s, text: %s, ident: %s, page: %s", menu_selection, text, ident, page)
+ lang = config.plugins.tmdb.lang.value
+ totalpages = 0
+ json_data = {}
+ if menu_selection == 1:
+ json_data = tmdb.Movies().now_playing(page=page, language=lang)
+ elif menu_selection == 2:
+ json_data = tmdb.Movies().upcoming(page=page, language=lang)
+ elif menu_selection == 3:
+ json_data = tmdb.Movies().popular(page=page, language=lang)
+ elif menu_selection == 4:
+ json_data = tmdb.Movies(ident).similar_movies(page=page, language=lang)
+ elif menu_selection == 5:
+ json_data = tmdb.Movies(ident).recommendations(page=page, language=lang)
+ elif menu_selection == 6:
+ json_data = tmdb.Movies().top_rated(page=page, language=lang)
+ else:
+ json_data = tmdb.Search().multi(query=text, language=lang)
+
+ results = {}
+ self.parseJson(results, json_data, ["total_pages", "results"])
+ totalpages = results["total_pages"]
+ for entry in results["results"]:
+ logger.debug("entry: %s", entry)
+ result = {}
+ keys = ["media_type", "id", "title", "name", "release_date", "first_air_date", "poster_path", "backdrop_path", "profile_path"]
+ self.parseJson(result, entry, keys)
+
+ media = result["media_type"]
+ ident = result["id"]
+ title_movie = result["title"]
+ title_series = result["name"]
+ title_person = result["name"]
+ date_movie = result["release_date"]
+ date_tv = result["first_air_date"]
+ cover_path = result["poster_path"]
+ profile_path = result["profile_path"]
+ backdrop_path = result["backdrop_path"]
+
+ title = search_title = ""
+ if media == "movie" and title_movie:
+ title = "%s (%s, %s)" % (title_movie, _("Movie"), date_movie[:4])
+ search_title = title_movie
+ elif media == "tv" and title_series:
+ title = "%s (%s, %s)" % (title_series, _("Series"), date_tv[:4])
+ search_title = title_series
+ elif media == "person" and title_person:
+ title = "%s (%s)" % (title_person, _("Person"))
+ search_title = title_person
+ elif menu_selection and title_movie:
+ media = "movie"
+ title = "%s (%s, %s)" % (title_movie, _("Movie"), date_movie[:4])
+ search_title = title_movie
+ else:
+ media = ""
+
+ if media == "person":
+ cover_url = "http://image.tmdb.org/t/p/%s%s" % (config.plugins.tmdb.cover_size.value, profile_path)
+ else:
+ cover_url = "http://image.tmdb.org/t/p/%s%s" % (config.plugins.tmdb.cover_size.value, cover_path)
+ backdrop_url = "http://image.tmdb.org/t/p/%s%s" % (config.plugins.tmdb.backdrop_size.value, backdrop_path)
+
+ logger.debug("ident: %s, title: %s, media: %s", ident, title, media)
+ if ident and title and media:
+ res.append(((title, ident, media, cover_url, backdrop_url, search_title), ))
+ del json_data
+ return totalpages, res
diff --git a/src/SearchMovie.py b/src/SearchMovie.py
new file mode 100644
index 0000000..e881666
--- /dev/null
+++ b/src/SearchMovie.py
@@ -0,0 +1,87 @@
+#!/usr/bin/python
+# coding=utf-8
+#
+# Copyright (C) 2018-2024 by dream-alpha
+#
+# In case of reuse of this source code please do not remove this copyright.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# For more information on the GNU General Public License see:
+# .
+
+from Components.config import config
+from . import tmdbsimple as tmdb
+from .__init__ import _
+from .Debug import logger
+from .Json import Json
+from .Parsers import Parsers
+
+
+class SearchMovie(Parsers, Json):
+ def __init__(self):
+ Parsers.__init__(self)
+ Json.__init__(self)
+
+ def getResult(self, result, ident, media):
+ logger.debug("ident: %s, media: %s", ident, media)
+ json_data = {}
+ keys_movie = ["title", "original_title", "overview", "year", "vote_average", "vote_count", "runtime", "production_countries", "production_companies", "genres", "tagline", "release_date", "seasons", "videos", "credits", "releases"]
+ keys_tv = keys_movie + ["name", "first_air_date", "origin_country", "created_by", "networks", "number_of_seasons", "number_of_episodes", "credits", "content_ratings"]
+ for lang in [config.plugins.tmdb.lang.value, "en"]:
+ if media == "movie":
+ json_data = tmdb.Movies(ident).info(language=lang, append_to_response="videos,credits,releases")
+ # logger.debug("json_data: %s", json_data)
+ self.parseJson(result, json_data, keys_movie)
+ if result["overview"]:
+ break
+ if media == "tv":
+ json_data = tmdb.TV(ident).info(language=lang, append_to_response="videos,credits,content_ratings")
+ # logger.debug("json_data: %s", json_data)
+ self.parseJson(result, json_data, keys_tv)
+ if result["overview"]:
+ break
+ del json_data
+
+ # base for movie and tv series
+ result["year"] = result["release_date"][:4]
+ result["rating"] = "%.1f" % float(result["vote_average"])
+ result["votes"] = str(result["vote_count"])
+ result["votes_brackets"] = "(%s)" % str(result["vote_count"])
+ result["runtime"] = "%s" % result["runtime"] + " " + _("min")
+
+ self.parseCountry(result)
+ self.parseGenre(result)
+ self.parseCast(result)
+ self.parseCrew(result)
+ self.parseStudio(result)
+ self.parseFsk(result, media)
+
+ if media == "movie":
+ result["seasons"] = ""
+ self.parseMovieVideos(result)
+
+ elif media == "tv":
+ # modify data for TV/Series
+ result["year"] = result["first_air_date"][:4]
+
+ self.parseTVCountry(result)
+ self.parseTVCrew(result)
+ self.parseTVStudio(result)
+ self.parseTVSeasons(result)
+
+ result["fulldescription"] = \
+ "%s\n" % result["tagline"] \
+ + "%s, %s, %s\n\n" % (result["genre"], result["country"], result["year"]) \
+ + "%s \n\n" % result["overview"] \
+ + "%s\n%s\n%s\n" % (result["cast"], result["crew"], result["seasons"])
+ logger.debug("result: %s", result)
+ return result
diff --git a/src/SearchPeople.py b/src/SearchPeople.py
new file mode 100644
index 0000000..1febbe4
--- /dev/null
+++ b/src/SearchPeople.py
@@ -0,0 +1,94 @@
+#!/usr/bin/python
+# coding=utf-8
+#
+# Copyright (C) 2018-2024 by dream-alpha
+#
+# In case of reuse of this source code please do not remove this copyright.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# For more information on the GNU General Public License see:
+# .
+
+
+from Components.config import config
+from . import tmdbsimple as tmdb
+from .Debug import logger
+from .Json import Json
+
+
+class SearchPeople(Json):
+ def __init__(self):
+ Json.__init__(self)
+
+ def getResult(self, res, ident, media):
+ logger.info("ident: %s", ident)
+ json_data = {}
+ lang = config.plugins.tmdb.lang.value
+ if media == "movie":
+ json_data = tmdb.Movies(ident).credits(language=lang)
+ logger.debug("json_data: %s", json_data)
+ if media == "tv":
+ json_data = tmdb.TV(ident).info(language=lang, append_to_result="credits")
+ logger.debug("json_data: %s", json_data)
+
+ result = {}
+ self.parseJson(result, json_data, ["cast", "seasons", "credits"])
+
+ for casts in result["cast"]:
+ result2 = {}
+ keys = ["id", "name", "profile_path", "character"]
+ self.parseJson(result2, casts, keys)
+ cover_ident = result2["id"]
+ name = result2["name"]
+ title = "%s (%s)" % (result2["name"], result2["character"])
+ cover_path = result2["profile_path"]
+ cover_url = "http://image.tmdb.org/t/p/%s/%s" % (config.plugins.tmdb.cover_size.value, cover_path)
+ if cover_ident and name != "None":
+ res.append(((title, name, cover_url, cover_ident), ))
+
+ if media == "tv":
+ season_number = 1
+ for season in result["seasons"]:
+ # logger.debug("######: %s", season)
+ result2 = {}
+ keys2 = ["season_number", "id", "name", "air_date"]
+ self.parseJson(result2, season, keys2)
+ season_number = result2["season_number"]
+ # logger.debug("#########: %s", result2["season_number"])
+ cover_ident = result2["id"]
+ name = result2["name"]
+ date = result2["air_date"][:4]
+ title = "%s (%s)" % (name, date)
+ logger.debug("title: %s", title)
+ logger.debug("name: %s", name)
+ if name != "None":
+ res.append(((title, name, None, ""), ))
+
+ json_data = tmdb.TV_Seasons(ident, season_number).credits(language=lang)
+ result3 = {}
+ self.parseJson(result3, json_data, ["cast"])
+ for casts in result3["cast"]:
+ result4 = {}
+ keys4 = ["id", "name", "character", "profile_path"]
+ self.parseJson(result4, casts, keys4)
+ cover_ident = result4["id"]
+ name = result4["name"]
+ character = result4["character"]
+ title = " %s (%s)" % (name, character)
+ cover_path = result4["profile_path"]
+ cover_url = "http://image.tmdb.org/t/p/%s%s" % (config.plugins.tmdb.cover_size.value, cover_path)
+
+ if cover_ident and name != "None":
+ res.append(((title, name, cover_url, cover_ident), ))
+ del json_data
+ logger.debug("res: %s", res)
+ return res
diff --git a/src/SearchPerson.py b/src/SearchPerson.py
new file mode 100644
index 0000000..f2969cf
--- /dev/null
+++ b/src/SearchPerson.py
@@ -0,0 +1,73 @@
+#!/usr/bin/python
+# coding=utf-8
+#
+# Copyright (C) 2018-2024 by dream-alpha
+#
+# In case of reuse of this source code please do not remove this copyright.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# For more information on the GNU General Public License see:
+# .
+
+
+from Components.config import config
+from . import tmdbsimple as tmdb
+from .Debug import logger
+from .Json import Json
+from .Parsers import Parsers
+
+
+class SearchPerson(Parsers, Json):
+ def __init__(self):
+ Parsers.__init__(self)
+ Json.__init__(self)
+
+ def getResult(self, result, ident):
+ lang = config.plugins.tmdb.lang.value
+ logger.debug("ident: %s", ident)
+ keys = ["biography", "name", "birthday", "place_of_birth", "gender", "also_known_as", "popularity", "movie_credits", "tv_credits"]
+ for lang in [config.plugins.tmdb.lang.value, "en"]:
+ json_data = tmdb.People(ident).info(language=lang, append_to_response="movie_credits, tv_credits")
+ # logger.debug("json_data: %s", json_data)
+ self.parseJson(result, json_data, keys)
+ if result["biography"]:
+ break
+
+ logger.debug("result: %s", result)
+
+ self.parsePersonGender(result)
+ self.parseJsonList(result, "also_known_as", ",")
+ result["popularity"] = "%.1f" % float(result["popularity"])
+
+ data_movies = []
+ for source in [
+ (result["movie_credits"], ["release_date", "title", "character"], "movie"),
+ (result["tv_credits"], ["first_air_date", "name", "character"], "tv")]:
+ result2 = {}
+ self.parseJson(result2, source[0], ["cast"])
+ logger.debug("result2: %s", result2)
+ for cast in result2["cast"]:
+ logger.debug("cast: %s", cast)
+ movie = {}
+ self.parseJson(movie, cast, source[1])
+ logger.debug("movie: %s", movie)
+ if source[2] == "movie":
+ if movie["release_date"] != "None":
+ data_movies.append(("%s %s (%s)" % (movie["release_date"], movie["title"], movie["character"])))
+ else:
+ if movie["first_air_date"] != "None":
+ data_movies.append(("%s %s (%s)" % (movie["first_air_date"], movie["name"], movie["character"])))
+ data_movies.sort(reverse=True)
+ movies = "\n".join(data_movies)
+ result["movies"] = movies
+ del json_data
+ return result
diff --git a/src/SearchSeason.py b/src/SearchSeason.py
new file mode 100644
index 0000000..b5d4c7d
--- /dev/null
+++ b/src/SearchSeason.py
@@ -0,0 +1,72 @@
+#!/usr/bin/python
+# coding=utf-8
+#
+# Copyright (C) 2018-2024 by dream-alpha
+#
+# In case of reuse of this source code please do not remove this copyright.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# For more information on the GNU General Public License see:
+# .
+
+from Components.config import config
+from . import tmdbsimple as tmdb
+from .Debug import logger
+from .Json import Json
+
+
+class SearchSeason(Json):
+ def __init__(self):
+ Json.__init__(self)
+
+ def getResult(self, res, ident):
+ logger.info("ident: %s", ident)
+ lang = config.plugins.tmdb.lang.value
+ json_data = tmdb.TV(ident).info(language=lang)
+ result = {}
+ self.parseJson(result, json_data, ["seasons"])
+ for seasons in result["seasons"]:
+ result1a = {}
+ self.parseJson(result1a, seasons, ["season_number", "id"])
+ season_ident = result1a["id"]
+ season_number = result1a["season_number"]
+ logger.debug("season_number: %s", season_number)
+
+ json_data = tmdb.TV_Seasons(ident, season_number).info(language=lang)
+ logger.debug("json_data: %s", json_data)
+ result2 = {}
+ self.parseJson(result2, json_data, ["name", "air_date", "title", "overview", "poster_path", "episodes"])
+ air_date = "(%s)" % result2["air_date"][:4]
+ title = result2["name"]
+ title = "%s %s" % (title, air_date)
+ overview = result2["overview"]
+ cover_path = result2["poster_path"]
+ logger.debug("cover_path: %s", cover_path)
+ cover_url = "http://image.tmdb.org/t/p/%s/%s" % (config.plugins.tmdb.cover_size.value, cover_path)
+ if ident and title:
+ res.append(((title, cover_url, overview, season_ident), ))
+
+ for names in result2["episodes"]:
+ result2a = {}
+ self.parseJson(result2a, names, ["id", "name", "title", "episode_number", "overview", "still_path"])
+ episode_ident = result2a["id"]
+ title = result2a["episode_number"]
+ name = result2a["name"]
+ title = "%+6s %s" % (title, name)
+ overview = result2a["overview"]
+ cover_path = result2a["still_path"]
+ logger.debug("cover_path: %s", cover_path)
+ cover_url = "http://image.tmdb.org/t/p/%s/%s" % (config.plugins.tmdb.cover_size.value, cover_path)
+ if ident and title:
+ res.append(((title, cover_url, overview, episode_ident), ))
+ del json_data
+ return res
diff --git a/src/SearchTMDB.py b/src/SearchTMDB.py
new file mode 100644
index 0000000..049e5e1
--- /dev/null
+++ b/src/SearchTMDB.py
@@ -0,0 +1,58 @@
+#!/usr/bin/python
+# coding=utf-8
+#
+# Copyright (C) 2018-2024 by dream-alpha
+#
+# In case of reuse of this source code please do not remove this copyright.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# For more information on the GNU General Public License see:
+# .
+
+from Components.config import config
+from . import tmdbsimple as tmdb
+from .Debug import logger
+from .Json import Json
+
+
+class SearchTMDB(Json):
+
+ def __init__(self):
+ Json.__init__(self)
+
+ def getResult(self, res, text):
+ logger.info("text: >%s<", text)
+ lang = config.plugins.tmdb.lang.value
+ json_data = {}
+ results = {}
+ media = "movie"
+ json_data = tmdb.Search().multi(query=text, language=lang)
+ self.parseJson(results, json_data, ["results"])
+ logger.debug("json_data: %s", json_data)
+
+ for entry in results["results"]:
+ logger.debug("entry: %s", entry)
+ result = {}
+ keys = ["media_type", "id", "title", "original_title", "name", "release_date", "first_air_date", "poster_path", "backdrop_path", "profile_path"]
+ self.parseJson(result, entry, keys)
+
+ ident = result["id"]
+ title = result["title"] if media == "movie" else result["name"]
+ cover_url = "http://image.tmdb.org/t/p/%s%s" % (config.plugins.tmdb.cover_size.value, result["poster_path"])
+ backdrop_url = "http://image.tmdb.org/t/p/%s%s" % (config.plugins.tmdb.backdrop_size.value, result["backdrop_path"])
+
+ logger.debug("ident: %s, title: %s, media: %s", ident, title, media)
+ if ident and title and media:
+ res.append(((title, ident, media, cover_url, backdrop_url), ))
+ break
+ del json_data
+ return res
diff --git a/src/SkinUtils.py b/src/SkinUtils.py
new file mode 100644
index 0000000..1228983
--- /dev/null
+++ b/src/SkinUtils.py
@@ -0,0 +1,96 @@
+#!/usr/bin/python
+# coding=utf-8
+#
+# Copyright (C) 2018-2024 by dream-alpha
+#
+# In case of reuse of this source code please do not remove this copyright.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# For more information on the GNU General Public License see:
+# .
+
+
+import os
+from enigma import getDesktop
+from Components.config import config
+from Tools.Directories import resolveFilename, SCOPE_PLUGINS
+from skin import loadSkin, loadSingleSkinData, dom_skins
+from .Debug import logger
+from .Version import ID, PLUGIN
+
+
+def getSkinName(skin_name):
+ return ID + skin_name
+
+
+def getScalingFactor():
+ return {"HD": 2.0 / 3.0, "FHD": 1, "WQHD": 4.0 / 3.0}[getResolution()]
+
+
+def getResolution():
+ height = getDesktop(0).size().height()
+ resolution = "SD"
+ if height > 576:
+ resolution = "HD"
+ if height > 720:
+ resolution = "FHD"
+ if height > 1080:
+ resolution = "WQHD"
+ return resolution
+
+
+def getSkinPath(file_name):
+ logger.debug(">>> file_name: %s", file_name)
+ base_skin_dir = "/usr/share/enigma2"
+ sub_skin_dir = os.path.dirname(config.skin.primary_skin.value)
+ resolution = getResolution()
+ logger.debug("resolution: %s, sub_skin_dir: %s", resolution, sub_skin_dir)
+ if not sub_skin_dir:
+ sub_skin_dir = "Default-HD"
+ elif resolution == "FHD":
+ if sub_skin_dir in ["Shadow-FHD", "Zombi-Shadow-FHD"]:
+ sub_skin_dir = "Shadow-FHD"
+ else:
+ sub_skin_dir = "Default-FHD"
+ elif resolution == "WQHD":
+ if sub_skin_dir in ["Shadow-WQHD", "Default-WQHD"]:
+ sub_skin_dir = "Default-WQHD"
+ else:
+ sub_skin_dir = "Other-WQHD"
+ else:
+ sub_skin_dir = "Default-HD"
+
+ dirs = [
+ os.path.join(resolveFilename(SCOPE_PLUGINS), "Extensions", PLUGIN, "skin", sub_skin_dir),
+ os.path.join(resolveFilename(SCOPE_PLUGINS), "SystemPlugins", PLUGIN, "skin", sub_skin_dir),
+ os.path.join(resolveFilename(SCOPE_PLUGINS), "Extensions", PLUGIN, "skin"),
+ os.path.join(resolveFilename(SCOPE_PLUGINS), "SystemPlugins", PLUGIN, "skin"),
+ os.path.join(base_skin_dir, sub_skin_dir),
+ base_skin_dir
+ ]
+ logger.debug("dirs: %s", dirs)
+
+ for adir in dirs:
+ skin_path = os.path.join(adir, file_name)
+ logger.debug("checking: skin_path: %s", skin_path)
+ if os.path.exists(skin_path):
+ break
+ skin_path = ""
+ logger.debug("skin_path: %s", skin_path)
+ return skin_path
+
+
+def loadPluginSkin(skin_file):
+ logger.info("skin_path: %s", getSkinPath(skin_file))
+ loadSkin(getSkinPath(skin_file), "")
+ path, dom_skin = dom_skins[-1:][0]
+ loadSingleSkinData(getDesktop(0), dom_skin, path)
diff --git a/src/TMDBCockpit.png b/src/TMDBCockpit.png
new file mode 100755
index 0000000..2100045
Binary files /dev/null and b/src/TMDBCockpit.png differ
diff --git a/src/Utils.py b/src/Utils.py
new file mode 100755
index 0000000..4e0ee33
--- /dev/null
+++ b/src/Utils.py
@@ -0,0 +1,94 @@
+#!/usr/bin/python
+# coding=utf-8
+#
+# Copyright (C) 2018-2024 by dream-alpha
+#
+# In case of reuse of this source code please do not remove this copyright.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# For more information on the GNU General Public License see:
+# .
+
+
+import os
+import re
+import base64
+from .FileUtils import readFile
+from .Debug import logger
+
+
+temp_dir = "/var/volatile/tmp/TMDBCockpit/"
+api_key_file = "/etc/enigma2/tmdb_key.txt"
+
+
+def getApiKey():
+ api_key = ""
+ if os.path.isfile(api_key_file):
+ api_key = readFile(api_key_file)[:32]
+ if not api_key:
+ api_key = base64.b64decode("M2I2NzAzYjg3MzRmZWUxYjU5OGRlOWVkN2JiZDNiNDc=")
+ # logger.debug(api_key: %s", api_key)
+ return api_key
+
+
+def cleanText(text):
+ logger.debug("text 1: %s", text)
+
+ text = re.sub(r'\(.*\)', '', text).rstrip() # remove (xyz)"
+ logger.debug("text 2: %s", text)
+
+ unwanted = [":", "-", "_", ",", ".", "+", "[", "]", "(", ")"]
+ for char in unwanted:
+ text = text.replace(char, " ")
+ logger.debug("text 3: %s", text)
+
+ text = " ".join(text.split()) # remove multiple spaces
+ logger.debug("text 4: >%s<", text)
+ return text
+
+
+def checkText(text):
+ # tuples indicate the bottom and top of the range, inclusive
+ cjk_ranges = [
+ (0x0600, 0x06FF), # arabic
+ (0x0750, 0x97FF),
+ (0xAC00, 0xD7AF), # hangul
+ (0x4E00, 0x62FF), # chinese
+ (0x6300, 0x77FF),
+ (0x7800, 0x8CFF),
+ (0x8D00, 0x9FCC),
+ (0x3400, 0x4DB5),
+ (0x20000, 0x215FF),
+ (0x21600, 0x230FF),
+ (0x23100, 0x245FF),
+ (0x24600, 0x260FF),
+ (0x26100, 0x275FF),
+ (0x27600, 0x290FF),
+ (0x29100, 0x2A6DF),
+ (0x2A700, 0x2B734),
+ (0x2B740, 0x2B81D),
+ (0x2B820, 0x2CEAF),
+ (0x2CEB0, 0x2EBEF),
+ (0x2F800, 0x2FA1F),
+ ]
+
+ def is_cjk(char):
+ char = ord(char)
+ for bottom, top in cjk_ranges:
+ if bottom <= char <= top:
+ return True
+ return False
+
+ res = text
+ if any(map(is_cjk, text)):
+ res = ""
+ return res
diff --git a/src/Version.py b/src/Version.py
new file mode 100644
index 0000000..c80c813
--- /dev/null
+++ b/src/Version.py
@@ -0,0 +1,26 @@
+#!/usr/bin/python
+# coding=utf-8
+#
+# Copyright (C) 2018-2024 by dream-alpha
+#
+# In case of reuse of this source code please do not remove this copyright.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# For more information on the GNU General Public License see:
+# .
+
+
+PLUGIN = "TMDBCockpit"
+ID = "TMDB"
+VERSION = "8.9.4"
+COPYRIGHT = "2018-2024 by dream-alpha"
+LICENSE = "This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version."
diff --git a/src/WebRequests.py b/src/WebRequests.py
new file mode 100644
index 0000000..52b193a
--- /dev/null
+++ b/src/WebRequests.py
@@ -0,0 +1,101 @@
+#!/usr/bin/python
+# coding=utf-8
+#
+# Copyright (C) 2018-2024 by dream-alpha
+#
+# In case of reuse of this source code please do not remove this copyright.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# For more information on the GNU General Public License see:
+# .
+
+
+import json
+import random
+import requests
+from .FileUtils import writeFile
+from .Debug import logger
+
+
+class Content():
+ def __init__(self):
+ self.text = ""
+ self.status_code = "999"
+
+
+class WebRequests():
+
+ def __init__(self):
+ return
+
+ def getUserAgent(self):
+ user_agents = [
+ 'Mozilla/5.0 (compatible; Konqueror/4.5; FreeBSD) KHTML/4.5.4 (like Gecko)',
+ 'Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; WOW64; Trident/6.0)',
+ 'Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 7.1; Trident/5.0)',
+ 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/36.0.1985.67 Safari/537.36',
+ 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:29.0) Gecko/20120101 Firefox/29.0',
+ 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:33.0) Gecko/20100101 Firefox/33.0',
+ 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:35.0) Gecko/20120101 Firefox/35.0',
+ 'Mozilla/5.0 (Windows NT 6.3; rv:36.0) Gecko/20100101 Firefox/36.0',
+ 'Mozilla/5.0 (X11; Linux x86_64; rv:28.0) Gecko/20100101 Firefox/28.0',
+ 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/537.13+ (KHTML, like Gecko) Version/5.1.7 Safari/534.57.2',
+ 'Opera/9.80 (Macintosh; Intel Mac OS X 10.6.8; U; de) Presto/2.9.168 Version/11.52',
+ 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:91.0) Gecko/20100101 Firefox/91.0'
+ ]
+ user_agent = random.choice(user_agents)
+ return user_agent
+
+ def getSession(self):
+ session = requests.Session()
+ session.headers.update({"user-agent": self.getUserAgent()})
+ return session
+
+ def postContent(self, url, data=None):
+ # logger.info("url: %s", url)
+ headers = {"user-agent": self.getUserAgent(), "Content-Type": "text/plain"}
+ if data is None:
+ data = {}
+ try:
+ content = requests.post(url, headers=headers, data=json.dumps(data), allow_redirects=True, verify=False)
+ logger.debug("content.url: %s", content.url)
+ logger.debug("content.status_code: %s", content.status_code)
+ content.raise_for_status()
+ except Exception as e:
+ logger.error("exception: %s", e)
+ content = Content()
+ logger.debug("content.text: %s", content.text)
+ return content
+
+ def getContent(self, url, params=None):
+ # logger.info("url: %s", url)
+ headers = {"user-agent": self.getUserAgent()}
+ if params is None:
+ params = {}
+ try:
+ response = requests.get(url, headers=headers, params=params, allow_redirects=True, verify=False)
+ logger.debug("response.url: %s", response.url)
+ logger.debug("response.status_code: %s", response.status_code)
+ content = response.content
+ response.raise_for_status()
+ # except Exception as e:
+ except Exception:
+ # logger.error("exception: %s", e)
+ content = ""
+ return content
+
+ def downloadFile(self, url, path):
+ # logger.info("url: %s, path: %s", url, path)
+ content = self.getContent(url)
+ if content:
+ writeFile(path, content)
+ return content != ""
diff --git a/src/__init__.py b/src/__init__.py
new file mode 100644
index 0000000..edcc6e0
--- /dev/null
+++ b/src/__init__.py
@@ -0,0 +1,47 @@
+#!/usr/bin/python
+# coding=utf-8
+#
+# Copyright (C) 2018-2024 by dream-alpha
+#
+# In case of reuse of this source code please do not remove this copyright.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# For more information on the GNU General Public License see:
+# .
+
+
+import os
+import gettext
+from Components.Language import language
+from Tools.Directories import resolveFilename, SCOPE_PLUGINS
+from .Version import PLUGIN
+from .Debug import initLogging
+
+
+def initLocale():
+ lang = language.getLanguage()[:2]
+ os.environ["LANGUAGE"] = lang
+ locale = resolveFilename(SCOPE_PLUGINS, "Extensions/" + PLUGIN + "/locale")
+ if not os.path.exists(locale):
+ locale = resolveFilename(SCOPE_PLUGINS, "SystemPlugins/" + PLUGIN + "/locale")
+ if os.path.exists(locale):
+ gettext.bindtextdomain(PLUGIN, locale)
+
+
+def _(txt):
+ translation = gettext.dgettext(PLUGIN, txt)
+ return translation
+
+
+initLogging()
+initLocale()
+language.addCallback(initLocale)
diff --git a/src/keymap.xml b/src/keymap.xml
new file mode 100644
index 0000000..bf1b03c
--- /dev/null
+++ b/src/keymap.xml
@@ -0,0 +1,42 @@
+
+
+
diff --git a/src/locale/ar/LC_MESSAGES/TMDBCockpit.mo b/src/locale/ar/LC_MESSAGES/TMDBCockpit.mo
new file mode 100644
index 0000000..310bd1a
Binary files /dev/null and b/src/locale/ar/LC_MESSAGES/TMDBCockpit.mo differ
diff --git a/src/locale/de/LC_MESSAGES/TMDBCockpit.mo b/src/locale/de/LC_MESSAGES/TMDBCockpit.mo
new file mode 100644
index 0000000..ed4a447
Binary files /dev/null and b/src/locale/de/LC_MESSAGES/TMDBCockpit.mo differ
diff --git a/src/locale/es/LC_MESSAGES/TMDBCockpit.mo b/src/locale/es/LC_MESSAGES/TMDBCockpit.mo
new file mode 100644
index 0000000..c516ce3
Binary files /dev/null and b/src/locale/es/LC_MESSAGES/TMDBCockpit.mo differ
diff --git a/src/locale/it/LC_MESSAGES/TMDBCockpit.mo b/src/locale/it/LC_MESSAGES/TMDBCockpit.mo
new file mode 100644
index 0000000..a7f8b2b
Binary files /dev/null and b/src/locale/it/LC_MESSAGES/TMDBCockpit.mo differ
diff --git a/src/locale/nl/LC_MESSAGES/TMDBCockpit.mo b/src/locale/nl/LC_MESSAGES/TMDBCockpit.mo
new file mode 100644
index 0000000..d90083a
Binary files /dev/null and b/src/locale/nl/LC_MESSAGES/TMDBCockpit.mo differ
diff --git a/src/locale/ru/LC_MESSAGES/TMDBCockpit.mo b/src/locale/ru/LC_MESSAGES/TMDBCockpit.mo
new file mode 100644
index 0000000..c5ffd7d
Binary files /dev/null and b/src/locale/ru/LC_MESSAGES/TMDBCockpit.mo differ
diff --git a/src/locale/tr/LC_MESSAGES/TMDBCockpit.mo b/src/locale/tr/LC_MESSAGES/TMDBCockpit.mo
new file mode 100644
index 0000000..496bf77
Binary files /dev/null and b/src/locale/tr/LC_MESSAGES/TMDBCockpit.mo differ
diff --git a/src/plugin.py b/src/plugin.py
new file mode 100644
index 0000000..0cc4903
--- /dev/null
+++ b/src/plugin.py
@@ -0,0 +1,125 @@
+#!/usr/bin/python
+# coding=utf-8
+#
+# Copyright (C) 2018-2024 by dream-alpha
+#
+# In case of reuse of this source code please do not remove this copyright.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# For more information on the GNU General Public License see:
+# .
+
+
+from Components.config import config
+from Plugins.Plugin import PluginDescriptor
+from .__init__ import _
+from .Debug import logger
+from .Version import VERSION
+from .ConfigInit import ConfigInit
+from .EpgSelection import initEPGSelection
+from .ScreenMain import ScreenMain
+from .ScreenTMDB import ScreenTMDB
+from .SkinUtils import loadPluginSkin
+from .PluginUtils import WHERE_TMDB_SEARCH, WHERE_TMDB_MOVIELIST
+
+
+def showEventInfos(session, event="", service="", **__):
+ if not service:
+ service = session.nav.getCurrentService()
+ info = service.info()
+ if not event:
+ event = info.getEvent(0) # 0 = now, 1 = next
+ event_name = event and event.getEventName() or info.getName() or ""
+ session.open(ScreenMain, event_name, 2)
+
+
+def queryEventInfos(search, callback, **__):
+ logger.info("search: %s", search)
+ ScreenTMDB(search, callback)
+
+
+def movieList(session, service, **kwargs):
+ logger.info("...")
+ callback = kwargs["callback"] if "callback" in kwargs else None
+ if callback:
+ session.openWithCallback(callback, ScreenMain, service, 1)
+ else:
+ session.open(ScreenMain, service, 1)
+
+
+def main(session, **__):
+ session.open(ScreenMain, "", 3)
+
+
+def autoStart(reason, **kwargs):
+ if reason == 0: # startup
+ if "session" in kwargs:
+ logger.info("+++ Version: %s starts...", VERSION)
+ # session = kwargs["session"]
+ if config.plugins.tmdb.key_yellow.value:
+ initEPGSelection()
+ loadPluginSkin("skin.xml")
+ elif reason == 1: # shutdown
+ logger.info("--- shutdown")
+ else:
+ logger.info("reason not handled: %s", reason)
+
+
+def Plugins(**__):
+ ConfigInit()
+
+ descriptors = [
+ PluginDescriptor(
+ where=[
+ PluginDescriptor.WHERE_AUTOSTART,
+ PluginDescriptor.WHERE_SESSIONSTART,
+ ],
+ fnc=autoStart
+ ),
+ PluginDescriptor(
+ name="TMDBCockpit",
+ description=_("TMDB Infos"),
+ where=[
+ WHERE_TMDB_MOVIELIST,
+ PluginDescriptor.WHERE_MOVIELIST,
+ ],
+ fnc=movieList
+ ),
+ PluginDescriptor(
+ name="TMDBCockpit",
+ description=_("TMDB Infos"),
+ where=[
+ WHERE_TMDB_SEARCH,
+ ],
+ fnc=queryEventInfos
+ ),
+ PluginDescriptor(
+ name=_("TMDB Infos"),
+ description=_("TMDB Infos"),
+ where=[
+ PluginDescriptor.WHERE_EVENTINFO,
+ PluginDescriptor.WHERE_EVENTVIEW,
+ PluginDescriptor.WHERE_EPG_SELECTION_SINGLE_BLUE,
+ ],
+ fnc=showEventInfos
+ ),
+ PluginDescriptor(
+ name=_("TMDBCockpit"),
+ description=_("TMDB Infos"),
+ where=[
+ PluginDescriptor.WHERE_PLUGINMENU,
+ ],
+ icon="TMDBCockpit.png",
+ fnc=main
+ )
+ ]
+ return descriptors
diff --git a/src/skin/Default-FHD/Makefile.am b/src/skin/Default-FHD/Makefile.am
new file mode 100644
index 0000000..d9bb821
--- /dev/null
+++ b/src/skin/Default-FHD/Makefile.am
@@ -0,0 +1,2 @@
+installdir = $(libdir)/enigma2/python/Plugins/Extensions/TMDBCockpit/skin/Default-FHD
+install_DATA = skin.xml
diff --git a/src/skin/Default-FHD/colors.xmlinc b/src/skin/Default-FHD/colors.xmlinc
new file mode 100644
index 0000000..239e7c9
--- /dev/null
+++ b/src/skin/Default-FHD/colors.xmlinc
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/skin/Default-FHD/skin.xml b/src/skin/Default-FHD/skin.xml
new file mode 100644
index 0000000..b16e5a8
--- /dev/null
+++ b/src/skin/Default-FHD/skin.xml
@@ -0,0 +1,119 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Default
+
+
+ Date
+
+
+
+
+
diff --git a/src/skin/Default-HD/Makefile.am b/src/skin/Default-HD/Makefile.am
new file mode 100755
index 0000000..811ddb7
--- /dev/null
+++ b/src/skin/Default-HD/Makefile.am
@@ -0,0 +1,2 @@
+installdir = $(libdir)/enigma2/python/Plugins/Extensions/TMDBCockpit/skin/Default-HD
+install_DATA = skin.xml
diff --git a/src/skin/Default-HD/colors.xmlinc b/src/skin/Default-HD/colors.xmlinc
new file mode 100644
index 0000000..239e7c9
--- /dev/null
+++ b/src/skin/Default-HD/colors.xmlinc
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/skin/Default-HD/skin.xml b/src/skin/Default-HD/skin.xml
new file mode 100644
index 0000000..3822aa8
--- /dev/null
+++ b/src/skin/Default-HD/skin.xml
@@ -0,0 +1,119 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Default
+
+
+ Date
+
+
+
+
+
diff --git a/src/skin/Default-WQHD/Makefile.am b/src/skin/Default-WQHD/Makefile.am
new file mode 100755
index 0000000..7f87051
--- /dev/null
+++ b/src/skin/Default-WQHD/Makefile.am
@@ -0,0 +1,2 @@
+installdir = $(libdir)/enigma2/python/Plugins/Extensions/TMDBCockpit/skin/Default-WQHD
+install_DATA = skin.xml
diff --git a/src/skin/Default-WQHD/colors.xmlinc b/src/skin/Default-WQHD/colors.xmlinc
new file mode 100644
index 0000000..23b5d66
--- /dev/null
+++ b/src/skin/Default-WQHD/colors.xmlinc
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/skin/Default-WQHD/screen_ScreenConfig.xmlinc b/src/skin/Default-WQHD/screen_ScreenConfig.xmlinc
new file mode 100644
index 0000000..d37d51c
--- /dev/null
+++ b/src/skin/Default-WQHD/screen_ScreenConfig.xmlinc
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/src/skin/Default-WQHD/screen_ScreenMain.xmlinc b/src/skin/Default-WQHD/screen_ScreenMain.xmlinc
new file mode 100644
index 0000000..7249963
--- /dev/null
+++ b/src/skin/Default-WQHD/screen_ScreenMain.xmlinc
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/skin/Default-WQHD/screen_ScreenMovie.xmlinc b/src/skin/Default-WQHD/screen_ScreenMovie.xmlinc
new file mode 100644
index 0000000..4a1945a
--- /dev/null
+++ b/src/skin/Default-WQHD/screen_ScreenMovie.xmlinc
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/src/skin/Default-WQHD/screen_ScreenPeople.xmlinc b/src/skin/Default-WQHD/screen_ScreenPeople.xmlinc
new file mode 100644
index 0000000..b5f059d
--- /dev/null
+++ b/src/skin/Default-WQHD/screen_ScreenPeople.xmlinc
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/src/skin/Default-WQHD/screen_ScreenPerson.xmlinc b/src/skin/Default-WQHD/screen_ScreenPerson.xmlinc
new file mode 100644
index 0000000..0c2fc23
--- /dev/null
+++ b/src/skin/Default-WQHD/screen_ScreenPerson.xmlinc
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/src/skin/Default-WQHD/screen_ScreenSeason.xmlinc b/src/skin/Default-WQHD/screen_ScreenSeason.xmlinc
new file mode 100644
index 0000000..1d2609e
--- /dev/null
+++ b/src/skin/Default-WQHD/screen_ScreenSeason.xmlinc
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/skin/Default-WQHD/screenpart_Backdrop.xmlinc b/src/skin/Default-WQHD/screenpart_Backdrop.xmlinc
new file mode 100644
index 0000000..0158bba
--- /dev/null
+++ b/src/skin/Default-WQHD/screenpart_Backdrop.xmlinc
@@ -0,0 +1,2 @@
+
+
diff --git a/src/skin/Default-WQHD/screenpart_MovieInfo.xmlinc b/src/skin/Default-WQHD/screenpart_MovieInfo.xmlinc
new file mode 100644
index 0000000..7961c0f
--- /dev/null
+++ b/src/skin/Default-WQHD/screenpart_MovieInfo.xmlinc
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/skin/Default-WQHD/screenpart_Rating.xmlinc b/src/skin/Default-WQHD/screenpart_Rating.xmlinc
new file mode 100644
index 0000000..410c270
--- /dev/null
+++ b/src/skin/Default-WQHD/screenpart_Rating.xmlinc
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/src/skin/Default-WQHD/skin.xml b/src/skin/Default-WQHD/skin.xml
new file mode 100644
index 0000000..c1a850a
--- /dev/null
+++ b/src/skin/Default-WQHD/skin.xml
@@ -0,0 +1,118 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/skin/Makefile.am b/src/skin/Makefile.am
new file mode 100644
index 0000000..63e0851
--- /dev/null
+++ b/src/skin/Makefile.am
@@ -0,0 +1 @@
+SUBDIRS = images Default-HD Default-FHD Shadow-FHD Default-WQHD Other-WQHD
diff --git a/src/skin/Other-WQHD/Makefile.am b/src/skin/Other-WQHD/Makefile.am
new file mode 100644
index 0000000..f00ed02
--- /dev/null
+++ b/src/skin/Other-WQHD/Makefile.am
@@ -0,0 +1,2 @@
+installdir = $(libdir)/enigma2/python/Plugins/Extensions/TMDBCockpit/skin/Other-WQHD
+install_DATA = skin.xml
diff --git a/src/skin/Other-WQHD/colors.xmlinc b/src/skin/Other-WQHD/colors.xmlinc
new file mode 100644
index 0000000..88b18fa
--- /dev/null
+++ b/src/skin/Other-WQHD/colors.xmlinc
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/skin/Other-WQHD/skin.xml b/src/skin/Other-WQHD/skin.xml
new file mode 100644
index 0000000..6bd49b6
--- /dev/null
+++ b/src/skin/Other-WQHD/skin.xml
@@ -0,0 +1,119 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Default
+
+
+ Date
+
+
+
+
+
diff --git a/src/skin/Shadow-FHD/Makefile.am b/src/skin/Shadow-FHD/Makefile.am
new file mode 100644
index 0000000..428f873
--- /dev/null
+++ b/src/skin/Shadow-FHD/Makefile.am
@@ -0,0 +1,2 @@
+installdir = $(libdir)/enigma2/python/Plugins/Extensions/TMDBCockpit/skin/Shadow-FHD
+install_DATA = skin.xml
diff --git a/src/skin/Shadow-FHD/colors.xmlinc b/src/skin/Shadow-FHD/colors.xmlinc
new file mode 100644
index 0000000..218bc31
--- /dev/null
+++ b/src/skin/Shadow-FHD/colors.xmlinc
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/skin/Shadow-FHD/screen_ScreenConfig.xmlinc b/src/skin/Shadow-FHD/screen_ScreenConfig.xmlinc
new file mode 100644
index 0000000..9e78a34
--- /dev/null
+++ b/src/skin/Shadow-FHD/screen_ScreenConfig.xmlinc
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/src/skin/Shadow-FHD/screen_ScreenMain.xmlinc b/src/skin/Shadow-FHD/screen_ScreenMain.xmlinc
new file mode 100644
index 0000000..fd2979e
--- /dev/null
+++ b/src/skin/Shadow-FHD/screen_ScreenMain.xmlinc
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/skin/Shadow-FHD/screen_ScreenMovie.xmlinc b/src/skin/Shadow-FHD/screen_ScreenMovie.xmlinc
new file mode 100644
index 0000000..5c123b5
--- /dev/null
+++ b/src/skin/Shadow-FHD/screen_ScreenMovie.xmlinc
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/src/skin/Shadow-FHD/screen_ScreenPeople.xmlinc b/src/skin/Shadow-FHD/screen_ScreenPeople.xmlinc
new file mode 100644
index 0000000..cecd2fa
--- /dev/null
+++ b/src/skin/Shadow-FHD/screen_ScreenPeople.xmlinc
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/src/skin/Shadow-FHD/screen_ScreenPerson.xmlinc b/src/skin/Shadow-FHD/screen_ScreenPerson.xmlinc
new file mode 100644
index 0000000..7e9c974
--- /dev/null
+++ b/src/skin/Shadow-FHD/screen_ScreenPerson.xmlinc
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/src/skin/Shadow-FHD/screen_ScreenSeason.xmlinc b/src/skin/Shadow-FHD/screen_ScreenSeason.xmlinc
new file mode 100644
index 0000000..4b94b09
--- /dev/null
+++ b/src/skin/Shadow-FHD/screen_ScreenSeason.xmlinc
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/skin/Shadow-FHD/screenpart_Backdrop.xmlinc b/src/skin/Shadow-FHD/screenpart_Backdrop.xmlinc
new file mode 100644
index 0000000..1a431e3
--- /dev/null
+++ b/src/skin/Shadow-FHD/screenpart_Backdrop.xmlinc
@@ -0,0 +1,2 @@
+
+
diff --git a/src/skin/Shadow-FHD/screenpart_MovieInfo.xmlinc b/src/skin/Shadow-FHD/screenpart_MovieInfo.xmlinc
new file mode 100644
index 0000000..127efb4
--- /dev/null
+++ b/src/skin/Shadow-FHD/screenpart_MovieInfo.xmlinc
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/skin/Shadow-FHD/screenpart_Rating.xmlinc b/src/skin/Shadow-FHD/screenpart_Rating.xmlinc
new file mode 100644
index 0000000..d486456
--- /dev/null
+++ b/src/skin/Shadow-FHD/screenpart_Rating.xmlinc
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/src/skin/Shadow-FHD/skin.xml b/src/skin/Shadow-FHD/skin.xml
new file mode 100644
index 0000000..4d4b1b7
--- /dev/null
+++ b/src/skin/Shadow-FHD/skin.xml
@@ -0,0 +1,118 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/skin/images/Makefile.am b/src/skin/images/Makefile.am
new file mode 100644
index 0000000..de2d73f
--- /dev/null
+++ b/src/skin/images/Makefile.am
@@ -0,0 +1,2 @@
+installdir = $(libdir)/enigma2/python/Plugins/Extensions/TMDBCockpit/skin/images
+install_DATA = *.png *.jpg
diff --git a/src/skin/images/backdrop.jpg b/src/skin/images/backdrop.jpg
new file mode 100755
index 0000000..ea9eb48
Binary files /dev/null and b/src/skin/images/backdrop.jpg differ
diff --git a/src/skin/images/fsk_0.png b/src/skin/images/fsk_0.png
new file mode 100644
index 0000000..8f28e15
Binary files /dev/null and b/src/skin/images/fsk_0.png differ
diff --git a/src/skin/images/fsk_12.png b/src/skin/images/fsk_12.png
new file mode 100644
index 0000000..6eb7b74
Binary files /dev/null and b/src/skin/images/fsk_12.png differ
diff --git a/src/skin/images/fsk_16.png b/src/skin/images/fsk_16.png
new file mode 100644
index 0000000..125a053
Binary files /dev/null and b/src/skin/images/fsk_16.png differ
diff --git a/src/skin/images/fsk_18.png b/src/skin/images/fsk_18.png
new file mode 100644
index 0000000..0b0ca33
Binary files /dev/null and b/src/skin/images/fsk_18.png differ
diff --git a/src/skin/images/fsk_6.png b/src/skin/images/fsk_6.png
new file mode 100644
index 0000000..127a9a9
Binary files /dev/null and b/src/skin/images/fsk_6.png differ
diff --git a/src/skin/images/star.png b/src/skin/images/star.png
new file mode 100644
index 0000000..81f976f
Binary files /dev/null and b/src/skin/images/star.png differ
diff --git a/src/skin/images/tmdb.png b/src/skin/images/tmdb.png
new file mode 100755
index 0000000..9fbc1c0
Binary files /dev/null and b/src/skin/images/tmdb.png differ
diff --git a/src/skin/screen_ScreenConfig.xmlinc b/src/skin/screen_ScreenConfig.xmlinc
new file mode 100644
index 0000000..fadc561
--- /dev/null
+++ b/src/skin/screen_ScreenConfig.xmlinc
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/src/skin/screen_ScreenMain.xmlinc b/src/skin/screen_ScreenMain.xmlinc
new file mode 100644
index 0000000..012c4dc
--- /dev/null
+++ b/src/skin/screen_ScreenMain.xmlinc
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/skin/screen_ScreenMovie.xmlinc b/src/skin/screen_ScreenMovie.xmlinc
new file mode 100644
index 0000000..101c228
--- /dev/null
+++ b/src/skin/screen_ScreenMovie.xmlinc
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/skin/screen_ScreenPeople.xmlinc b/src/skin/screen_ScreenPeople.xmlinc
new file mode 100644
index 0000000..80f899e
--- /dev/null
+++ b/src/skin/screen_ScreenPeople.xmlinc
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/skin/screen_ScreenPerson.xmlinc b/src/skin/screen_ScreenPerson.xmlinc
new file mode 100644
index 0000000..e6efba9
--- /dev/null
+++ b/src/skin/screen_ScreenPerson.xmlinc
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/skin/screen_ScreenSeason.xmlinc b/src/skin/screen_ScreenSeason.xmlinc
new file mode 100644
index 0000000..8b57cc3
--- /dev/null
+++ b/src/skin/screen_ScreenSeason.xmlinc
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/skin/screenpart_1Button_icon.xmlinc b/src/skin/screenpart_1Button_icon.xmlinc
new file mode 100644
index 0000000..7fdff5f
--- /dev/null
+++ b/src/skin/screenpart_1Button_icon.xmlinc
@@ -0,0 +1 @@
+
diff --git a/src/skin/screenpart_1Button_name.xmlinc b/src/skin/screenpart_1Button_name.xmlinc
new file mode 100644
index 0000000..99ded21
--- /dev/null
+++ b/src/skin/screenpart_1Button_name.xmlinc
@@ -0,0 +1 @@
+
diff --git a/src/skin/screenpart_2Buttons.xmlinc b/src/skin/screenpart_2Buttons.xmlinc
new file mode 100644
index 0000000..0e8f51d
--- /dev/null
+++ b/src/skin/screenpart_2Buttons.xmlinc
@@ -0,0 +1,11 @@
+
+
+
+
+
+ Default
+
+
+ Date
+
+
diff --git a/src/skin/screenpart_4Buttons_icon.xmlinc b/src/skin/screenpart_4Buttons_icon.xmlinc
new file mode 100644
index 0000000..3494407
--- /dev/null
+++ b/src/skin/screenpart_4Buttons_icon.xmlinc
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/src/skin/screenpart_4Buttons_name.xmlinc b/src/skin/screenpart_4Buttons_name.xmlinc
new file mode 100644
index 0000000..478a313
--- /dev/null
+++ b/src/skin/screenpart_4Buttons_name.xmlinc
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/src/skin/screenpart_Background.xmlinc b/src/skin/screenpart_Background.xmlinc
new file mode 100644
index 0000000..73bed6f
--- /dev/null
+++ b/src/skin/screenpart_Background.xmlinc
@@ -0,0 +1,3 @@
+
+
+
diff --git a/src/skin/screenpart_TitleOnly.xmlinc b/src/skin/screenpart_TitleOnly.xmlinc
new file mode 100644
index 0000000..8360b2d
--- /dev/null
+++ b/src/skin/screenpart_TitleOnly.xmlinc
@@ -0,0 +1 @@
+
diff --git a/src/skin/skin_src.xml b/src/skin/skin_src.xml
new file mode 100644
index 0000000..5e763ff
--- /dev/null
+++ b/src/skin/skin_src.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/tmdbsimple/Makefile.am b/src/tmdbsimple/Makefile.am
new file mode 100644
index 0000000..dc741ac
--- /dev/null
+++ b/src/tmdbsimple/Makefile.am
@@ -0,0 +1,2 @@
+installdir = $(libdir)/enigma2/python/Plugins/Extensions/TMDBCockpit/tmdbsimple
+install_PYTHON = *.py
diff --git a/src/tmdbsimple/Version.py b/src/tmdbsimple/Version.py
new file mode 100644
index 0000000..6351a2c
--- /dev/null
+++ b/src/tmdbsimple/Version.py
@@ -0,0 +1,23 @@
+#!/usr/bin/python
+# coding=utf-8
+#
+# Copyright (C) 2018-2024 by dream-alpha
+#
+# In case of reuse of this source code please do not remove this copyright.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# For more information on the GNU General Public License see:
+# .
+
+
+PLUGIN = "tmdb"
+ID = "TMDB"
diff --git a/src/tmdbsimple/__init__.py b/src/tmdbsimple/__init__.py
new file mode 100644
index 0000000..630dcda
--- /dev/null
+++ b/src/tmdbsimple/__init__.py
@@ -0,0 +1,56 @@
+# -*- coding: utf-8 -*-
+
+"""
+tmdbsimple
+~~~~~~~~~~
+
+*tmdbsimple* is a wrapper, written in Python, for The Movie Database (TMDb)
+API v3. By calling the functions available in *tmdbsimple* you can simplify
+your code and easily access a vast amount of movie, tv, and cast data. To find
+out more about The Movie Database API, check out the overview page
+http://www.themoviedb.org/documentation/api and documentation page
+https://developers.themoviedb.org/3/getting-started
+https://www.themoviedb.org/documentation/api/status-codes
+
+:copyright: (c) 2013-2022 by Celia Oakley.
+:license: GPLv3, see LICENSE for more details
+"""
+
+__title__ = 'tmdbsimple'
+__version__ = '2.9.1'
+__author__ = 'Celia Oakley'
+__copyright__ = 'Copyright (c) 2013-2022 Celia Oakley'
+__license__ = 'GPLv3'
+
+import os
+import requests
+
+from .account import Account, Authentication, GuestSessions, Lists
+from .base import APIKeyError
+from .changes import Changes
+from .configuration import Configuration, Certifications
+from .discover import Discover
+from .find import Find, Trending
+from .genres import Genres
+from .movies import Movies, Collections, Companies, Keywords, Reviews
+from .people import People, Credits
+from .search import Search
+from .tv import TV, TV_Seasons, TV_Episodes, TV_Episode_Groups, TV_Changes, Networks
+
+__all__ = ['Account', 'Authentication', 'GuestSessions', 'Lists',
+ 'APIKeyError',
+ 'Changes',
+ 'Configuration', 'Certifications',
+ 'Discover',
+ 'Find', 'Trending',
+ 'Genres',
+ 'Movies', 'Collections', 'Companies', 'Keywords', 'Reviews',
+ 'People', 'Credits'
+ 'Search',
+ 'TV', 'TV_Seasons', 'TV_Episodes', 'TV_Episode_Groups', 'TV_Changes', 'Networks'
+ ]
+
+API_KEY = os.environ.get('TMDB_API_KEY', None)
+API_VERSION = '3'
+REQUESTS_SESSION = None
+REQUESTS_TIMEOUT = os.environ.get('TMDB_REQUESTS_TIMEOUT', None)
diff --git a/src/tmdbsimple/account.py b/src/tmdbsimple/account.py
new file mode 100644
index 0000000..d17003f
--- /dev/null
+++ b/src/tmdbsimple/account.py
@@ -0,0 +1,622 @@
+# -*- coding: utf-8 -*-
+
+"""
+tmdbsimple.account
+~~~~~~~~~~~~~~~~~~
+This module implements the Account, Authentication, and Lists functionality
+of tmdbsimple.
+
+Created by Celia Oakley on 2013-10-31.
+
+:copyright: (c) 2013-2022 by Celia Oakley
+:license: GPLv3, see LICENSE for more details
+"""
+
+from .base import TMDB
+
+
+class Account(TMDB):
+ """
+ Account functionality.
+
+ See: https://developers.themoviedb.org/3/account
+ https://www.themoviedb.org/documentation/api/sessions
+ """
+ BASE_PATH = 'account'
+ URLS = {
+ 'info': '',
+ 'lists': '/{id}/lists',
+ 'favorite_movies': '/{id}/favorite/movies',
+ 'favorite_tv': '/{id}/favorite/tv',
+ 'favorite': '/{id}/favorite',
+ 'rated_movies': '/{id}/rated/movies',
+ 'rated_tv': '/{id}/rated/tv',
+ 'rated_tv_episodes': '/{id}/rated/tv/episodes',
+ 'watchlist_movies': '/{id}/watchlist/movies',
+ 'watchlist_tv': '/{id}/watchlist/tv',
+ 'watchlist': '/{id}/watchlist',
+ }
+
+ def __init__(self, session_id):
+ super(Account, self).__init__()
+ self.session_id = session_id
+
+ def info(self, **kwargs):
+ """
+ Get your account details.
+
+ Args:
+
+ Returns:
+ A dict respresentation of the JSON returned from the API.
+ """
+ path = self._get_path('info')
+ kwargs.update({'session_id': self.session_id})
+
+ response = self._GET(path, kwargs)
+ self.id = response['id']
+ self._set_attrs_to_values(response)
+ return response
+
+ def lists(self, **kwargs):
+ """
+ Get all of the lists created by an account. Will include private lists
+ if you are the owner.
+
+ Args:
+ language: (optional) ISO 639-1 code.
+ page: (optional) Minimum 1, maximum 1000, default 1.
+
+ Returns:
+ A dict respresentation of the JSON returned from the API.
+ """
+ path = self._get_id_path('lists')
+ kwargs.update({'session_id': self.session_id})
+
+ response = self._GET(path, kwargs)
+ self._set_attrs_to_values(response)
+ return response
+
+ def favorite_movies(self, **kwargs):
+ """
+ Get the list of your favorite movies.
+
+ Args:
+ language: (optional) ISO 639-1 code.
+ sort_by: (optional) Allowed Values: created_at.asc, created_at.desc
+ page: (optional) Minimum 1, maximum 1000, default 1.
+
+ Returns:
+ A dict respresentation of the JSON returned from the API.
+ """
+ path = self._get_id_path('favorite_movies')
+ kwargs.update({'session_id': self.session_id})
+
+ response = self._GET(path, kwargs)
+ self._set_attrs_to_values(response)
+ return response
+
+ def favorite_tv(self, **kwargs):
+ """
+ Get the list of your favorite TV shows.
+
+ Args:
+ language: (optional) ISO 639-1 code.
+ sort_by: (optional) Allowed Values: created_at.asc, created_at.desc
+ page: (optional) Minimum 1, maximum 1000, default 1.
+
+ Returns:
+ A dict respresentation of the JSON returned from the API.
+ """
+ path = self._get_id_path('favorite_tv')
+ kwargs.update({'session_id': self.session_id})
+
+ response = self._GET(path, kwargs)
+ self._set_attrs_to_values(response)
+ return response
+
+ def favorite(self, **kwargs):
+ """
+ This method allows you to mark a movie or TV show as a favorite item.
+
+ Args:
+ media_type: 'movie' | 'tv'
+ media_id: The id of the media.
+ favorite: True (to add) | False (to remove).
+
+ Returns:
+ A dict respresentation of the JSON returned from the API.
+ """
+ path = self._get_id_path('favorite')
+ kwargs.update({'session_id': self.session_id})
+
+ payload = {
+ 'media_type': kwargs.pop('media_type', None),
+ 'media_id': kwargs.pop('media_id', None),
+ 'favorite': kwargs.pop('favorite', None),
+ }
+
+ response = self._POST(path, kwargs, payload)
+ self._set_attrs_to_values(response)
+ return response
+
+ def rated_movies(self, **kwargs):
+ """
+ Get a list of all the movies you have rated.
+
+ Args:
+ language: (optional) ISO 639-1 value.
+ sort_by: (optional) Allowed Values: created_at.asc, created_at.desc
+ page: (optional) Minimum 1, maximum 1000, default 1.
+
+ Returns:
+ A dict respresentation of the JSON returned from the API.
+ """
+ path = self._get_id_path('rated_movies')
+ kwargs.update({'session_id': self.session_id})
+
+ response = self._GET(path, kwargs)
+ self._set_attrs_to_values(response)
+ return response
+
+ def rated_tv(self, **kwargs):
+ """
+ Get a list of all the TV shows you have rated.
+
+ Args:
+ language: (optional) ISO 639-1 value.
+ sort_by: (optional) Allowed Values: created_at.asc, created_at.desc
+ page: (optional) Minimum 1, maximum 1000, default 1.
+
+ Returns:
+ A dict respresentation of the JSON returned from the API.
+ """
+ path = self._get_id_path('rated_tv')
+ kwargs.update({'session_id': self.session_id})
+
+ response = self._GET(path, kwargs)
+ self._set_attrs_to_values(response)
+ return response
+
+ def rated_tv_episodes(self, **kwargs):
+ """
+ Get a list of all the TV episodes you have rated.
+
+ Args:
+ language: (optional) ISO 639-1 value.
+ sort_by: (optional) Allowed Values: created_at.asc, created_at.desc
+ page: (optional) Minimum 1, maximum 1000, default 1.
+
+ Returns:
+ A dict respresentation of the JSON returned from the API.
+ """
+ path = self._get_id_path('rated_tv_episodes')
+ kwargs.update({'session_id': self.session_id})
+
+ response = self._GET(path, kwargs)
+ self._set_attrs_to_values(response)
+ return response
+
+ def watchlist_movies(self, **kwargs):
+ """
+ Get a list of all the movies you have added to your watchlist.
+
+ Args:
+ language: (optional) ISO 639-1 value.
+ sort_by: (optional) Allowed Values: created_at.asc, created_at.desc
+ page: (optional) Minimum 1, maximum 1000, default 1.
+
+ Returns:
+ A dict respresentation of the JSON returned from the API.
+ """
+ path = self._get_id_path('watchlist_movies')
+ kwargs.update({'session_id': self.session_id})
+
+ response = self._GET(path, kwargs)
+ self._set_attrs_to_values(response)
+ return response
+
+ def watchlist_tv(self, **kwargs):
+ """
+ Get a list of all the TV shows you have added to your watchlist.
+
+ Args:
+ language: (optional) ISO 639-1 value.
+ sort_by: (optional) Allowed Values: created_at.asc, created_at.desc
+ page: (optional) Minimum 1, maximum 1000, default 1.
+
+ Returns:
+ A dict respresentation of the JSON returned from the API.
+ """
+ path = self._get_id_path('watchlist_tv')
+ kwargs.update({'session_id': self.session_id})
+
+ response = self._GET(path, kwargs)
+ self._set_attrs_to_values(response)
+ return response
+
+ def watchlist(self, **kwargs):
+ """
+ Add a movie or TV show to your watchlist.
+
+ Args:
+ media_type: 'movie' | 'tv'
+ media_id: The id of the media.
+ watchlist: True (to add) | False (to remove).
+
+ Returns:
+ A dict respresentation of the JSON returned from the API.
+ """
+ path = self._get_id_path('watchlist')
+ kwargs.update({'session_id': self.session_id})
+
+ payload = {
+ 'media_type': kwargs.pop('media_type', None),
+ 'media_id': kwargs.pop('media_id', None),
+ 'watchlist': kwargs.pop('watchlist', None),
+ }
+
+ response = self._POST(path, kwargs, payload)
+ self._set_attrs_to_values(response)
+ return response
+
+
+class Authentication(TMDB):
+ """
+ Authentication functionality.
+
+ See: https://developers.themoviedb.org/3/authentication
+ https://www.themoviedb.org/documentation/api/sessions
+ """
+ BASE_PATH = 'authentication'
+ URLS = {
+ 'guest_session_new': '/guest_session/new',
+ 'token_new': '/token/new',
+ 'session_new': '/session/new',
+ 'token_validate_with_login': '/token/validate_with_login',
+ 'session_delete': '/session',
+ }
+
+ def guest_session_new(self, **kwargs):
+ """
+ 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
+ (https://developers.themoviedb.org/3/authentication/how-do-i-generate-a-session-id).
+
+ 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.
+
+ Args:
+
+ Returns:
+ A dict respresentation of the JSON returned from the API.
+ """
+ path = self._get_path('guest_session_new')
+
+ response = self._GET(path, kwargs)
+ self._set_attrs_to_values(response)
+ return response
+
+ def token_new(self, **kwargs):
+ """
+ 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
+ (https://developers.themoviedb.org/3/authentication/how-do-i-generate-a-session-id).
+
+ Args:
+
+ Returns:
+ A dict respresentation of the JSON returned from the API.
+ """
+ path = self._get_path('token_new')
+
+ response = self._GET(path, kwargs)
+ self._set_attrs_to_values(response)
+ return response
+
+ def session_new(self, **kwargs):
+ """
+ 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
+ (https://developers.themoviedb.org/3/authentication/how-do-i-generate-a-session-id).
+
+ Args:
+ request_token: The token you generated for the user to approve.
+ The token needs to be approved before being
+ used here.
+
+ Returns:
+ A dict respresentation of the JSON returned from the API.
+ """
+ path = self._get_path('session_new')
+
+ response = self._GET(path, kwargs)
+ self._set_attrs_to_values(response)
+ return response
+
+ def token_validate_with_login(self, **kwargs):
+ """
+ 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
+ (https://developers.themoviedb.org/3/authentication/how-do-i-generate-a-session-id).
+
+ If you decide to use this method please use HTTPS.
+
+ Args:
+ username: The user's username on TMDb.
+ password: The user's password on TMDb.
+ request_token: The token you generated for the user to approve.
+
+ Returns:
+ A dict respresentation of the JSON returned from the API.
+ """
+ path = self._get_path('token_validate_with_login')
+
+ response = self._GET(path, kwargs)
+ self._set_attrs_to_values(response)
+ return response
+
+ def session_delete(self, **kwargs):
+ """
+ If you would like to delete (or "logout") from a session, call this
+ method with a valid session ID.
+
+ Args:
+
+ Returns:
+ A dict respresentation of the JSON returned from the API.
+ """
+ path = self._get_path('session_delete')
+
+ payload = {
+ 'session_id': kwargs.pop('session_id', None),
+ }
+
+ response = self._DELETE(path, kwargs, payload)
+ self._set_attrs_to_values(response)
+ return response
+
+
+class GuestSessions(TMDB):
+ """
+ Guest Sessions functionality.
+
+ See: https://developers.themoviedb.org/3/guest-sessions
+ """
+ BASE_PATH = 'guest_session'
+ URLS = {
+ 'rated_movies': '/{guest_session_id}/rated/movies',
+ 'rated_tv': '/{guest_session_id}/rated/tv',
+ 'rated_tv_episodes': '/{guest_session_id}/rated/tv/episodes',
+ }
+
+ def __init__(self, guest_session_id=0):
+ super(GuestSessions, self).__init__()
+ self.guest_session_id = guest_session_id
+
+ def rated_movies(self, **kwargs):
+ """
+ Get the rated movies for a guest session.
+
+ Args:
+ language: (optional) ISO 639-1 code.
+ sort_by: (optional) Allowed Values: created_at.asc, created_at.desc
+
+ Returns:
+ A dict respresentation of the JSON returned from the API.
+ """
+ path = self._get_guest_session_id_path('rated_movies')
+
+ response = self._GET(path, kwargs)
+ self._set_attrs_to_values(response)
+ return response
+
+ def rated_tv(self, **kwargs):
+ """
+ Get the rated TV shows for a guest session.
+
+ Args:
+ language: (optional) ISO 639-1 code.
+ sort_by: (optional) Allowed Values: created_at.asc, created_at.desc
+
+ Returns:
+ A dict respresentation of the JSON returned from the API.
+ """
+ path = self._get_guest_session_id_path('rated_tv')
+
+ response = self._GET(path, kwargs)
+ self._set_attrs_to_values(response)
+ return response
+
+ def rated_tv_episodes(self, **kwargs):
+ """
+ Get the rated TV episodes for a guest session.
+
+ Args:
+ language: (optional) ISO 639-1 code.
+ sort_by: (optional) Allowed Values: created_at.asc, created_at.desc
+
+ Returns:
+ A dict respresentation of the JSON returned from the API.
+ """
+ path = self._get_guest_session_id_path('rated_tv_episodes')
+
+ response = self._GET(path, kwargs)
+ self._set_attrs_to_values(response)
+ return response
+
+
+class Lists(TMDB):
+ """
+ Lists functionality.
+
+ See: https://developers.themoviedb.org/3/lists
+ """
+ BASE_PATH = 'list'
+ URLS = {
+ 'info': '/{id}',
+ 'item_status': '/{id}/item_status',
+ 'list_create': '',
+ 'add_item': '/{id}/add_item',
+ 'remove_item': '/{id}/remove_item',
+ 'list_clear': '/{id}/clear',
+ 'list_delete': '/{id}',
+ }
+
+ def __init__(self, id=0, session_id=0):
+ super(Lists, self).__init__()
+ self.id = id
+ self.session_id = session_id
+
+ def info(self, **kwargs):
+ """
+ Get the details of a list.
+
+ Args:
+ language: (optional) ISO 639-1 code.
+
+ Returns:
+ A dict respresentation of the JSON returned from the API.
+ """
+ path = self._get_id_path('info')
+
+ response = self._GET(path, kwargs)
+ self._set_attrs_to_values(response)
+ return response
+
+ def item_status(self, **kwargs):
+ """
+ You can use this method to check if a movie has already been added to
+ the list.
+
+ Args:
+ movie_id: The id of the movie. Minimum 1.
+
+ Returns:
+ A dict respresentation of the JSON returned from the API.
+ """
+ path = self._get_id_path('item_status')
+
+ response = self._GET(path, kwargs)
+ self._set_attrs_to_values(response)
+ return response
+
+ def list_create(self, **kwargs):
+ """
+ Create a list.
+
+ Args:
+ name: Name of the list.
+ description: Description of the list.
+ language: (optional) ISO 639-1 code.
+
+ Returns:
+ A dict respresentation of the JSON returned from the API.
+ """
+ path = self._get_path('list_create')
+ kwargs.update({'session_id': self.session_id})
+
+ payload = {
+ 'name': kwargs.pop('name', None),
+ 'description': kwargs.pop('description', None),
+ 'language': kwargs.pop('language', None),
+ }
+
+ response = self._POST(path, kwargs, payload)
+ self._set_attrs_to_values(response)
+ self.id = self.list_id
+ return response
+
+ def add_item(self, **kwargs):
+ """
+ Add a movie to a list.
+
+ Args:
+ media_id: A movie id. Minimum 1.
+
+ Returns:
+ A dict respresentation of the JSON returned from the API.
+ """
+ path = self._get_id_path('add_item')
+ kwargs.update({'session_id': self.session_id})
+
+ payload = {
+ 'media_id': kwargs.pop('media_id', None),
+ }
+
+ response = self._POST(path, kwargs, payload)
+ self._set_attrs_to_values(response)
+ return response
+
+ def remove_item(self, **kwargs):
+ """
+ Remove a movie from a list.
+
+ Args:
+ media_id: A movie id. Minimum 1.
+
+ Returns:
+ A dict respresentation of the JSON returned from the API.
+ """
+ path = self._get_id_path('remove_item')
+ kwargs.update({'session_id': self.session_id})
+
+ payload = {
+ 'media_id': kwargs.pop('media_id', None),
+ }
+
+ response = self._POST(path, kwargs, payload)
+ self._set_attrs_to_values(response)
+ return response
+
+ def list_clear(self, **kwargs):
+ """
+ Clear all of the items from a list.
+
+ Args:
+ confirm: True (do it) | False (don't do it)
+
+ Returns:
+ A dict respresentation of the JSON returned from the API.
+ """
+ path = self._get_id_path('list_clear')
+ kwargs.update({'session_id': self.session_id})
+
+ payload = {}
+
+ response = self._POST(path, kwargs, payload)
+ self._set_attrs_to_values(response)
+ return response
+
+ def list_delete(self, **kwargs):
+ """
+ Delete a list.
+
+ Args:
+ None
+
+ Returns:
+ A dict respresentation of the JSON returned from the API.
+ """
+ path = self._get_id_path('list_delete')
+ kwargs.update({'session_id': self.session_id})
+
+ response = self._DELETE(path, kwargs)
+ self._set_attrs_to_values(response)
+ return response
diff --git a/src/tmdbsimple/base.py b/src/tmdbsimple/base.py
new file mode 100644
index 0000000..e9f0c5c
--- /dev/null
+++ b/src/tmdbsimple/base.py
@@ -0,0 +1,137 @@
+# -*- coding: utf-8 -*-
+
+"""
+tmdbsimple.base
+~~~~~~~~~~~~~~~
+This module implements the base class of tmdbsimple.
+
+Created by Celia Oakley on 2013-10-31.
+
+:copyright: (c) 2013-2022 by Celia Oakley
+:license: GPLv3, see LICENSE for more details
+"""
+
+import json
+import requests
+from ..WebRequests import WebRequests
+
+
+class APIKeyError(Exception):
+ pass
+
+
+class TMDB(WebRequests, object):
+ headers = {'Content-Type': 'application/json',
+ 'Accept': 'application/json',
+ 'Connection': 'close'}
+ BASE_PATH = ''
+ URLS = {}
+
+ def __init__(self):
+ WebRequests.__init__(self)
+ from . import API_VERSION, REQUESTS_SESSION, REQUESTS_TIMEOUT
+ self.base_uri = 'https://api.themoviedb.org'
+ self.base_uri += '/{version}'.format(version=API_VERSION)
+ self.session = REQUESTS_SESSION
+ self.timeout = REQUESTS_TIMEOUT
+
+ def _get_path(self, key):
+ return self.BASE_PATH + self.URLS[key]
+
+ def _get_id_path(self, key):
+ return self._get_path(key).format(id=self.id)
+
+ def _get_guest_session_id_path(self, key):
+ return self._get_path(key).format(
+ guest_session_id=self.guest_session_id)
+
+ def _get_credit_id_path(self, key):
+ return self._get_path(key).format(credit_id=self.credit_id)
+
+ def _get_media_type_time_window_path(self, key):
+ return self._get_path(key).format(
+ media_type=self.media_type, time_window=self.time_window)
+
+ def _get_tv_id_season_number_path(self, key):
+ return self._get_path(key).format(
+ tv_id=self.tv_id, season_number=self.season_number)
+
+ def _get_tv_id_season_number_episode_number_path(self, key):
+ return self._get_path(key).format(
+ tv_id=self.tv_id, season_number=self.season_number,
+ episode_number=self.episode_number)
+
+ def _get_complete_url(self, path):
+ return '{base_uri}/{path}'.format(base_uri=self.base_uri, path=path)
+
+ def _get_params(self, params):
+ from . import API_KEY
+ if not API_KEY:
+ raise APIKeyError
+
+ api_dict = {'api_key': API_KEY}
+ if params:
+ params.update(api_dict)
+ for key, value in params.items():
+ if isinstance(params[key], bool):
+ params[key] = 'true' if value is True else 'false'
+
+ else:
+ params = api_dict
+ return params
+
+ def _request(self, method, path, params=None, payload=None):
+ url = self._get_complete_url(path)
+ params = self._get_params(params)
+
+ # Create a new request session if no global session is defined
+ if self.session is None:
+ response = requests.request(
+ method,
+ url,
+ params=params,
+ data=json.dumps(payload) if payload else payload,
+ headers=self.headers, timeout=self.timeout
+ )
+
+ # Use the global requests session the user provided
+ else:
+ response = self.session.request(
+ method,
+ url,
+ params=params,
+ data=json.dumps(payload) if payload else payload,
+ headers=self.headers, timeout=self.timeout
+ )
+
+ response.raise_for_status()
+ response.encoding = 'utf-8'
+ return response.json()
+
+ def _GET(self, path, params=None):
+ url = self._get_complete_url(path)
+ params = self._get_params(params)
+ content = self.getContent(url, params)
+ return json.loads(content)
+
+ def _POST(self, path, params=None, payload=None):
+ return self._request('POST', path, params=params, payload=payload)
+
+ def _DELETE(self, path, params=None, payload=None):
+ return self._request('DELETE', path, params=params, payload=payload)
+
+ def _set_attrs_to_values(self, response={}):
+ return
+
+ """
+ Set attributes to dictionary values.
+ - e.g.
+ >>> import tmdbsimple as tmdb
+ >>> movie = tmdb.Movies(103332)
+ >>> response = movie.info()
+ >>> movie.title # instead of response['title']
+ """
+ if isinstance(response, dict):
+ for key in response.keys():
+ if not hasattr(self, key) or not callable(getattr(self, key)):
+ setattr(self, key, response[key])
diff --git a/src/tmdbsimple/changes.py b/src/tmdbsimple/changes.py
new file mode 100644
index 0000000..3baabe4
--- /dev/null
+++ b/src/tmdbsimple/changes.py
@@ -0,0 +1,97 @@
+# -*- coding: utf-8 -*-
+
+"""
+tmdbsimple.changes
+~~~~~~~~~~~~~~~~~~
+This module implements the Changes functionality of tmdbsimple.
+
+Created by Celia Oakley on 2013-10-31.
+
+:copyright: (c) 2013-2022 by Celia Oakley
+:license: GPLv3, see LICENSE for more details
+"""
+
+from .base import TMDB
+
+
+class Changes(TMDB):
+ """
+ Changes functionality.
+
+ See: https://developers.themoviedb.org/3/changes
+ """
+ BASE_PATH = ''
+ URLS = {
+ 'movie': 'movie/changes',
+ 'tv': 'tv/changes',
+ 'person': 'person/changes',
+ }
+
+ def movie(self, **kwargs):
+ """
+ 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.
+ 100 items are returned per page.
+
+ Args:
+ start_date: (optional) Expected format is 'YYYY-MM-DD'.
+ end_date: (optional) Expected format is 'YYYY-MM-DD'.
+ page: (optional) Minimum 1, maximum 1000, default 1.
+
+ Returns:
+ A dict respresentation of the JSON returned from the API.
+ """
+ path = self._get_path('movie')
+
+ response = self._GET(path, kwargs)
+ self._set_attrs_to_values(response)
+ return response
+
+ def tv(self, **kwargs):
+ """
+ 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.
+ 100 items are returned per page.
+
+ Args:
+ start_date: (optional) Expected format is 'YYYY-MM-DD'.
+ end_date: (optional) Expected format is 'YYYY-MM-DD'.
+ page: (optional) Minimum 1, maximum 1000, default 1.
+
+ Returns:
+ A dict respresentation of the JSON returned from the API.
+ """
+ path = self._get_path('tv')
+
+ response = self._GET(path, kwargs)
+ self._set_attrs_to_values(response)
+ return response
+
+ def person(self, **kwargs):
+ """
+ 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.
+ 100 items are returned per page.
+
+ Args:
+ start_date: (optional) Expected format is 'YYYY-MM-DD'.
+ end_date: (optional) Expected format is 'YYYY-MM-DD'.
+ page: (optional) Minimum 1, maximum 1000, default 1.
+
+ Returns:
+ A dict respresentation of the JSON returned from the API.
+ """
+ path = self._get_path('person')
+
+ response = self._GET(path, kwargs)
+ self._set_attrs_to_values(response)
+ return response
diff --git a/src/tmdbsimple/configuration.py b/src/tmdbsimple/configuration.py
new file mode 100644
index 0000000..872e2ef
--- /dev/null
+++ b/src/tmdbsimple/configuration.py
@@ -0,0 +1,208 @@
+# -*- coding: utf-8 -*-
+
+"""
+tmdbsimple.configuration
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+This module implements the Configuration and Certifications functionality of
+tmdbsimple.
+
+Created by Celia Oakley on 2013-10-31.
+
+:copyright: (c) 2013-2022 by Celia Oakley
+:license: GPLv3, see LICENSE for more details
+"""
+
+from .base import TMDB
+
+
+class Configuration(TMDB):
+ """
+ Configuration functionality.
+
+ See: https://developers.themoviedb.org/3/configuration
+ """
+ BASE_PATH = 'configuration'
+ URLS = {
+ 'info': '',
+ 'countries': '/countries',
+ 'jobs': '/jobs',
+ 'languages': '/languages',
+ 'primary_translations': '/primary_translations',
+ 'timezones': '/timezones',
+ }
+
+ def info(self, **kwargs):
+ """
+ Get the system wide configuration information. Some elements of the API
+ require some knowledge of this configuration data. The purpose of this
+ is to try and keep the actual API 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:
+
+ 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.
+
+ Args:
+ None
+
+ Returns:
+ A dict respresentation of the JSON returned from the API.
+ """
+ path = self._get_path('info')
+
+ response = self._GET(path, kwargs)
+ self._set_attrs_to_values(response)
+ return response
+
+ def countries(self, **kwargs):
+ """
+ Get the list of countries (ISO 3166-1 tags) used throughout TMDb.
+
+ Args:
+ None
+
+ Returns:
+ A dict respresentation of the JSON returned from the API.
+ """
+ path = self._get_path('countries')
+
+ response = self._GET(path, kwargs)
+ self._set_attrs_to_values(response)
+ return response
+
+ def jobs(self, **kwargs):
+ """
+ Get a list of the jobs and departments we use on TMDb.
+
+ Args:
+ None
+
+ Returns:
+ A dict respresentation of the JSON returned from the API.
+ """
+ path = self._get_path('jobs')
+
+ response = self._GET(path, kwargs)
+ self._set_attrs_to_values(response)
+ return response
+
+ def languages(self, **kwargs):
+ """
+ Get the list of languages (ISO 639-1 tags) used throughout TMDb.
+
+ Args:
+ None
+
+ Returns:
+ A dict respresentation of the JSON returned from the API.
+ """
+ path = self._get_path('languages')
+
+ response = self._GET(path, kwargs)
+ self._set_attrs_to_values(response)
+ return response
+
+ def primary_translations(self, **kwargs):
+ """
+ Get a list of the officially supported translations on TMDb.
+
+ Args:
+ None
+
+ Returns:
+ A dict respresentation of the JSON returned from the API.
+ """
+ path = self._get_path('primary_translations')
+
+ response = self._GET(path, kwargs)
+ self._set_attrs_to_values(response)
+ return response
+
+ def timezones(self, **kwargs):
+ """
+ Get the list of timezones used throughout TMDb.
+
+ Args:
+ None
+
+ Returns:
+ A dict respresentation of the JSON returned from the API.
+ """
+ path = self._get_path('timezones')
+
+ response = self._GET(path, kwargs)
+ self._set_attrs_to_values(response)
+ return response
+
+
+class Certifications(TMDB):
+ """
+ Certifications functionality.
+
+ See: https://developers.themoviedb.org/3/certifications
+ """
+ BASE_PATH = 'certification'
+ URLS = {
+ 'movie_list': '/movie/list',
+ 'tv_list': '/tv/list',
+ }
+
+ def movie_list(self, **kwargs):
+ """
+ Get an up to date list of the officially supported movie certifications on TMDb.
+
+ Args:
+ None
+
+ Returns:
+ A dict respresentation of the JSON returned from the API.
+ """
+ path = self._get_path('movie_list')
+
+ response = self._GET(path, kwargs)
+ self._set_attrs_to_values(response)
+ return response
+
+ def tv_list(self, **kwargs):
+ """
+ Get an up to date list of the officially supported TV show certifications on TMDb.
+
+ Args:
+ None
+
+ Returns:
+ A dict respresentation of the JSON returned from the API.
+ """
+ path = self._get_path('tv_list')
+
+ response = self._GET(path, kwargs)
+ self._set_attrs_to_values(response)
+ return response
+
+ # backward compatability, when only /movie/list existed
+ def list(self, **kwargs):
+ """
+ Get the list of supported certifications for movies.
+
+ Args:
+ None
+
+ Returns:
+ A dict respresentation of the JSON returned from the API.
+ """
+ path = self._get_path('movie_list')
+
+ response = self._GET(path, kwargs)
+ self._set_attrs_to_values(response)
+ return response
diff --git a/src/tmdbsimple/discover.py b/src/tmdbsimple/discover.py
new file mode 100644
index 0000000..b10bf53
--- /dev/null
+++ b/src/tmdbsimple/discover.py
@@ -0,0 +1,254 @@
+# -*- coding: utf-8 -*-
+
+"""
+tmdbsimple.discover
+~~~~~~~~~~~~~~~~~~~
+This module implements the Discover functionality of tmdbsimple.
+
+Created by Celia Oakley on 2013-10-31.
+
+:copyright: (c) 2013-2022 by Celia Oakley
+:license: GPLv3, see LICENSE for more details
+"""
+
+from .base import TMDB
+
+
+class Discover(TMDB):
+ """
+ Discover functionality.
+
+ See: https://developers.themoviedb.org/3/discover
+ """
+ BASE_PATH = 'discover'
+ URLS = {
+ 'movie': '/movie',
+ 'tv': '/tv',
+ }
+
+ def movie(self, **kwargs):
+ """
+ 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 at
+ https://www.themoviedb.org/documentation/api/discover.
+
+ Args:
+ language: (optional) ISO 639-1 code.
+ region: (optional) Specify a ISO 3166-1 code.
+ sort_by: (optional) 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
+ Default: popularity.desc
+ certification_country: (optional) Used in conjunction with the
+ certification filter, use this to specify a country with a
+ valid certification.
+ certification: Filter results with a valid certification from the
+ 'certification_country' field.
+ certification.gte: Filter and only include movies that have a
+ certification that is greater than or equal to the specified
+ value.
+ certification.lte: Filter and only include movies that have a
+ certification that is less than or equal to the specified
+ value.
+ include_adult: (optional) A filter and include or exclude adult
+ movies.
+ include_video: (optional) A filter to include or exclude videos.
+ page: (optional) Minimum 1, maximum 1000, default 1.
+ primary_release_year: (optional) A filter to limit the results to a
+ specific primary release year.
+ primary_release_date.gte: (optional) Filter and only include movies
+ that have a primary release date that is greater or equal to
+ the specified value.
+ primary_release_date.lte: (optional) Filter and only include movies
+ that have a primary release date that is less than or equal to
+ the specified value.
+ release_date.gte: (optional) Filter and only include movies that
+ have a primary release date that is greater or equal to the
+ specified value.
+ releaste_date.lte: (optional) Filter and only include movies that
+ have a primary release date that is less than or equal to the
+ specified value.
+ with_release_type: (optional) 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.
+ Minimum 1, maximum 6.
+ year: (optional) A filter to limit the results to a specific year
+ (looking at all release dates).
+ vote_count.gte: (optional) Filter and only include movies that have
+ a vote count that is greater or equal to the specified value.
+ Minimum 0.
+ vote_count.lte: (optional) Filter and only include movies that have
+ a vote count that is less than or equal to the specified value.
+ Minimum 1.
+ vote_average.gte: (optional) Filter and only include movies that
+ have a rating that is greater or equal to the specified value.
+ Minimum 0.
+ vote_average.lte: (optional) Filter and only include movies that
+ have a rating that is less than or equal to the specified value.
+ Minimum 0.
+ with_cast: (optional) 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) 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) 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) 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) Comma separated value of genre ids that you
+ want to include in the results.
+ without_genres: (optional) Comma separated value of genre ids that
+ you want to exclude from the results.
+ with_keywords: (optional) 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) Exclude items with certain keywords.
+ You can comma and pipe seperate these values to create an 'AND' or 'OR' logic.
+ with_runtime.gte: (optional) Filter and only include movies that
+ have a runtime that is greater or equal to a value.
+ with_runtime.lte: (optional) Filter and only include movies that
+ have a runtime that is less than or equal to a value.
+ with_original_language: (optional) Specify an ISO 639-1 string to
+ filter results by their original language value.
+
+ Returns:
+ A dict respresentation of the JSON returned from the API.
+ """
+ # Periods are not allowed in keyword arguments but several API
+ # arguments contain periods. See both usages in tests/test_discover.py.
+ for param in dict(kwargs):
+ if '_lte' in param:
+ kwargs[param.replace('_lte', '.lte')] = kwargs.pop(param)
+ if '_gte' in param:
+ kwargs[param.replace('_gte', '.gte')] = kwargs.pop(param)
+
+ path = self._get_path('movie')
+
+ response = self._GET(path, kwargs)
+ self._set_attrs_to_values(response)
+ return response
+
+ def tv(self, **kwargs):
+ """
+ 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 at
+ https://www.themoviedb.org/documentation/api/discover.
+
+ Args:
+ language: (optional) ISO 639-1 code.
+ sort_by: (optional) Available options are 'vote_average.desc',
+ 'vote_average.asc', 'first_air_date.desc',
+ 'first_air_date.asc', 'popularity.desc', 'popularity.asc'
+ sort_by: (optional) Allowed values: vote_average.desc,
+ vote_average.asc, first_air_date.desc, first_air_date.asc,
+ popularity.desc, popularity.asc
+ Default: popularity.desc
+ air_date.gte: (optional) 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.
+ air_date.lte: (optional) 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.
+ first_air_date.gte: (optional) 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.
+ first_air_date.lte: (optional) 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.
+ first_air_date_year: (optional) 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) Specify the page of results to query. Default 1.
+ timezone: (optional) Used in conjunction with the air_date.gte/lte
+ filter to calculate the proper UTC offset. Default
+ America/New_York.
+ vote_average.gte: (optional) Filter and only include movies that
+ have a rating that is greater or equal to the specified value.
+ Minimum 0.
+ vote_count.gte: (optional) Filter and only include movies that have
+ a rating that is less than or equal to the specified value.
+ Minimum 0.
+ with_genres: (optional) Comma separated value of genre ids that you
+ want to include in the results.
+ with_networks: (optional) Comma separated value of network ids that
+ you want to include in the results.
+ without_genres: (optional) Comma separated value of genre ids that
+ you want to exclude from the results.
+ with_runtime.gte: (optional) Filter and only include TV shows with
+ an episode runtime that is greater than or equal to a value.
+ with_runtime.lte: (optional) 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) 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) Specify an ISO 639-1 string to
+ filter results by their original language value.
+ without_keywords: (optional) Exclude items with certain keywords.
+ You can comma and pipe seperate these values to create an 'AND'
+ or 'OR' logic.
+ screened_theatrically: (optional) Filter results to include items
+ that have been screened theatrically.
+ with_companies: (optional) 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) A comma separated list of keyword ID's.
+ Only includes TV shows that have one of the ID's added as a
+ keyword.
+
+ Returns:
+ A dict respresentation of the JSON returned from the API.
+ """
+ # Periods are not allowed in keyword arguments but several API
+ # arguments contain periods. See both usages in tests/test_discover.py.
+ for param in dict(kwargs):
+ if '_lte' in param:
+ kwargs[param.replace('_lte', '.lte')] = kwargs.pop(param)
+ if '_gte' in param:
+ kwargs[param.replace('_gte', '.gte')] = kwargs.pop(param)
+
+ path = self._get_path('tv')
+
+ response = self._GET(path, kwargs)
+ self._set_attrs_to_values(response)
+ return response
diff --git a/src/tmdbsimple/find.py b/src/tmdbsimple/find.py
new file mode 100644
index 0000000..b036505
--- /dev/null
+++ b/src/tmdbsimple/find.py
@@ -0,0 +1,105 @@
+# -*- coding: utf-8 -*-
+
+"""
+tmdbsimple.find
+~~~~~~~~~~~~~~~
+This module implements the Find functionality of tmdbsimple.
+
+Created by Celia Oakley on 2013-10-31.
+
+:copyright: (c) 2013-2022 by Celia Oakley
+:license: GPLv3, see LICENSE for more details
+"""
+
+from .base import TMDB
+
+
+class Find(TMDB):
+ """
+ Find functionality.
+
+ See: https://developers.themoviedb.org/3/find
+ """
+ BASE_PATH = 'find'
+ URLS = {
+ 'info': '/{id}',
+ }
+
+ def __init__(self, id=0):
+ super(Find, self).__init__()
+ self.id = id
+
+ def info(self, **kwargs):
+ """
+ The find method makes it easy to search for objects in our database by
+ an external id. For example, an IMDB 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.
+ Media Databases: IMDb ID, TVDB ID, Freebase MID*, Freebase ID*,
+ TVRage ID*
+ Social IDs: Facebook, Insagram, Twitter
+
+ Args:
+ language: (optional) ISO 639-1 code.
+ external_source: Allowed Values: imdb_id, freebase_mid,
+ freebase_id, tvdb_id, tvrage_id, facebook_id, twitter_id,
+ instagram_id
+
+ Returns:
+ A dict respresentation of the JSON returned from the API.
+ """
+ path = self._get_id_path('info')
+
+ response = self._GET(path, kwargs)
+ self._set_attrs_to_values(response)
+ return response
+
+
+class Trending(TMDB):
+ """
+ Trending functionality.
+
+ See: https://developers.themoviedb.org/3/trending
+ """
+ BASE_PATH = 'trending'
+ URLS = {
+ 'info': '/{media_type}/{time_window}',
+ }
+
+ def __init__(self, media_type='all', time_window='day'):
+ super(Trending, self).__init__()
+ self.media_type = media_type
+ self.time_window = time_window
+
+ def info(self, **kwargs):
+ """
+ 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.
+
+ Valid Media Types
+ '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.
+ 'people': Show the trending people in the results.
+
+ Valid Time Windows
+ 'day': View the trending list for the day.
+ 'week': View the trending list for the week.
+
+ Args:
+ None
+
+ Returns:
+ A dict respresentation of the JSON returned from the API.
+ """
+ path = self._get_media_type_time_window_path('info')
+
+ response = self._GET(path, kwargs)
+ self._set_attrs_to_values(response)
+ return response
diff --git a/src/tmdbsimple/genres.py b/src/tmdbsimple/genres.py
new file mode 100644
index 0000000..e25dcee
--- /dev/null
+++ b/src/tmdbsimple/genres.py
@@ -0,0 +1,88 @@
+# -*- coding: utf-8 -*-
+
+"""
+tmdbsimple.genres
+~~~~~~~~~~~~~~~~~
+This module implements the Genres functionality of tmdbsimple.
+
+Created by Celia Oakley on 2013-10-31.
+
+:copyright: (c) 2013-2022 by Celia Oakley
+:license: GPLv3, see LICENSE for more details
+"""
+
+from .base import TMDB
+
+
+class Genres(TMDB):
+ """
+ Genres functionality.
+
+ See: https://developers.themoviedb.org/3/genres
+ """
+ BASE_PATH = 'genre'
+ URLS = {
+ 'movie_list': '/movie/list',
+ 'tv_list': '/tv/list',
+ 'movies': '/{id}/movies', # backward compatability
+ }
+
+ def __init__(self, id=0):
+ super(Genres, self).__init__()
+ self.id = id
+
+ def movie_list(self, **kwargs):
+ """
+ Get the list of official genres for movies.
+
+ Args:
+ language: (optional) ISO 639-1 code.
+
+ Returns:
+ A dict respresentation of the JSON returned from the API.
+ """
+ path = self._get_path('movie_list')
+
+ response = self._GET(path, kwargs)
+ self._set_attrs_to_values(response)
+ return response
+
+ def tv_list(self, **kwargs):
+ """
+ Get the list of official genres for TV shows.
+
+ Args:
+ language: (optional) ISO 639-1 code.
+
+ Returns:
+ A dict respresentation of the JSON returned from the API.
+ """
+ path = self._get_path('tv_list')
+
+ response = self._GET(path, kwargs)
+ self._set_attrs_to_values(response)
+ return response
+
+ # backward compatability
+ def movies(self, **kwargs):
+ """
+ Get the list of movies for a particular genre by id. By default, only
+ movies with 10 or more votes are included.
+
+ Args:
+ page: (optional) Minimum 1, maximum 1000.
+ language: (optional) ISO 639-1 code.
+ include_all_movies: (optional) Toggle the inclusion of all movies
+ and not just those with 10 or more ratings.
+ Expected value is: True or False.
+ include_adult: (optional) Toggle the inclusion of adult titles.
+ Expected value is: True or False.
+
+ Returns:
+ A dict respresentation of the JSON returned from the API.
+ """
+ path = self._get_id_path('movies')
+
+ response = self._GET(path, kwargs)
+ self._set_attrs_to_values(response)
+ return response
diff --git a/src/tmdbsimple/movies.py b/src/tmdbsimple/movies.py
new file mode 100644
index 0000000..0f52a16
--- /dev/null
+++ b/src/tmdbsimple/movies.py
@@ -0,0 +1,774 @@
+# -*- coding: utf-8 -*-
+
+"""
+tmdbsimple.movies
+~~~~~~~~~~~~~~~~~
+This module implements the Movies, Collections, Companies, Keywords, and
+Reviews functionality of tmdbsimple.
+
+Created by Celia Oakley on 2013-10-31.
+
+:copyright: (c) 2013-2022 by Celia Oakley
+:license: GPLv3, see LICENSE for more details
+"""
+
+from .base import TMDB
+
+
+class Movies(TMDB):
+ """
+ Movies functionality.
+
+ See: https://developers.themoviedb.org/3/movies
+ """
+ BASE_PATH = 'movie'
+ URLS = {
+ 'info': '/{id}',
+ 'account_states': '/{id}/account_states',
+ 'alternative_titles': '/{id}/alternative_titles',
+ 'changes': '/{id}/changes',
+ 'credits': '/{id}/credits',
+ 'external_ids': '/{id}/external_ids',
+ 'images': '/{id}/images',
+ 'keywords': '/{id}/keywords',
+ 'lists': '/{id}/lists',
+ 'recommendations': '/{id}/recommendations',
+ 'release_dates': '/{id}/release_dates',
+ 'reviews': '/{id}/reviews',
+ 'similar_movies': '/{id}/similar_movies',
+ 'translations': '/{id}/translations',
+ 'videos': '/{id}/videos',
+ 'watch_providers': '/{id}/watch/providers',
+ 'rating': '/{id}/rating',
+ 'rating_delete': '/{id}/rating',
+ 'latest': '/latest',
+ 'now_playing': '/now_playing',
+ 'popular': '/popular',
+ 'top_rated': '/top_rated',
+ 'upcoming': '/upcoming',
+ 'releases': '/{id}/releases', # backward compatability
+ }
+
+ def __init__(self, id=0):
+ super(Movies, self).__init__()
+ self.id = id
+
+ def info(self, **kwargs):
+ """
+ Get the primary information about a movie.
+
+ Supports append_to_response. Read more about this at
+ https://developers.themoviedb.org/3/getting-started/append-to-response.
+
+ Args:
+ language: (optional) ISO 639-1 code.
+ append_to_response: (optional) Append requests within the same
+ namespace to the response.
+
+ Returns:
+ A dict representation of the JSON returned from the API.
+ """
+ path = self._get_id_path('info')
+
+ response = self._GET(path, kwargs)
+ self._set_attrs_to_values(response)
+ return response
+
+ def account_states(self, **kwargs):
+ """
+ Grab the following account states for a session:
+ - Movie rating
+ - If it belongs to your watchlist
+ - If it belongs to your favourite list
+
+ Args:
+ session_id: (required) See Authentication.
+ guest_session_id: (optional) See Authentication.
+
+ Returns:
+ A dict representation of the JSON returned from the API.
+ """
+ path = self._get_id_path('account_states')
+
+ response = self._GET(path, kwargs)
+ self._set_attrs_to_values(response)
+ return response
+
+ def alternative_titles(self, **kwargs):
+ """
+ Get all of the alternative titles for a movie.
+
+ Args:
+ country: (optional) ISO 3166-1 code.
+
+ Returns:
+ A dict representation of the JSON returned from the API.
+ """
+ path = self._get_id_path('alternative_titles')
+
+ response = self._GET(path, kwargs)
+ self._set_attrs_to_values(response)
+ return response
+
+ def changes(self, **kwargs):
+ """
+ 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.
+
+ Args:
+ start_date: (optional) Filter the results with a start date.
+ Expected format is 'YYYY-MM-DD'.
+ end_date: (optional) Filter the results with a end date.
+ Expected format is 'YYYY-MM-DD'.
+ page: (optional) Minimum 1, maximum 1000, default 1.
+
+ Returns:
+ A dict representation of the JSON returned from the API.
+ """
+ path = self._get_id_path('changes')
+
+ response = self._GET(path, kwargs)
+ self._set_attrs_to_values(response)
+ return response
+
+ def credits(self, **kwargs):
+ """
+ Get the cast and crew for a movie.
+
+ Args:
+ None
+
+ Returns:
+ A dict representation of the JSON returned from the API.
+ """
+ path = self._get_id_path('credits')
+
+ response = self._GET(path, kwargs)
+ self._set_attrs_to_values(response)
+ return response
+
+ def external_ids(self, **kwargs):
+ """
+ Get the external ids for a movie. We currently support the following
+ external sources.
+
+ Media Databases - IMDb
+ Social IDs - Facebok, Instagram, Twitter
+
+ Args:
+ None
+
+ Returns:
+ A dict representation of the JSON returned from the API.
+ """
+ path = self._get_id_path('external_ids')
+
+ response = self._GET(path, kwargs)
+ self._set_attrs_to_values(response)
+ return response
+
+ def images(self, **kwargs):
+ """
+ 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 seperated value like so:
+ include_image_language=en,null.
+
+ Args:
+ language: (optional) ISO 639-1 code.
+ include_image_language: (optional) Comma separated, a valid
+ ISO 69-1.
+
+ Returns:
+ A dict representation of the JSON returned from the API.
+ """
+ path = self._get_id_path('images')
+
+ response = self._GET(path, kwargs)
+ self._set_attrs_to_values(response)
+ return response
+
+ def keywords(self):
+ """
+ Get the keywords that have been added to a movie.
+
+ Args:
+ None
+
+ Returns:
+ A dict representation of the JSON returned from the API.
+ """
+ path = self._get_id_path('keywords')
+
+ response = self._GET(path)
+ self._set_attrs_to_values(response)
+ return response
+
+ def lists(self, **kwargs):
+ """
+ Get a list of lists that this movie belongs to.
+
+ Args:
+ language: (optional) ISO 639-1 code.
+ page: (optional) Minimum 1, maximum 1000, default 1.
+
+ Returns:
+ A dict representation of the JSON returned from the API.
+ """
+ path = self._get_id_path('lists')
+
+ response = self._GET(path, kwargs)
+ self._set_attrs_to_values(response)
+ return response
+
+ def recommendations(self, **kwargs):
+ """
+ Get a list of recommended movies for a movie.
+
+ Args:
+ language: (optional) ISO 639-1 code.
+ page: (optional) Minimum 1, maximum 1000, default 1.
+
+ Returns:
+ A dict representation of the JSON returned from the API.
+ """
+ path = self._get_id_path('recommendations')
+
+ response = self._GET(path, kwargs)
+ self._set_attrs_to_values(response)
+ return response
+
+ def release_dates(self, **kwargs):
+ """
+ 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
+
+ Args:
+ None
+
+ Returns:
+ A dict representation of the JSON returned from the API.
+ """
+ path = self._get_id_path('release_dates')
+
+ response = self._GET(path, kwargs)
+ self._set_attrs_to_values(response)
+ return response
+
+ def reviews(self, **kwargs):
+ """
+ Get the user reviews for a movie.
+
+ Args:
+ language: (optional) ISO 639-1 code.
+ page: (optional) Minimum 1, maximum 1000, default 1.
+
+ Returns:
+ A dict representation of the JSON returned from the API.
+ """
+ path = self._get_id_path('reviews')
+
+ response = self._GET(path, kwargs)
+ self._set_attrs_to_values(response)
+ return response
+
+ def similar_movies(self, **kwargs):
+ """
+ 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.
+
+ Args:
+ language: (optional) ISO 639-1 code.
+ page: (optional) Minimum 1, maximum 1000, default 1.
+
+ Returns:
+ A dict representation of the JSON returned from the API.
+ """
+ path = self._get_id_path('similar_movies')
+
+ response = self._GET(path, kwargs)
+ self._set_attrs_to_values(response)
+ return response
+
+ def translations(self, **kwargs):
+ """
+ Get a list of translations that have been created for a movie.
+
+ Args:
+ None
+
+ Returns:
+ A dict representation of the JSON returned from the API.
+ """
+ path = self._get_id_path('translations')
+
+ response = self._GET(path, kwargs)
+ self._set_attrs_to_values(response)
+ return response
+
+ def videos(self, **kwargs):
+ """
+ Get the videos that have been added to a movie.
+
+ Args:
+ language: (optional) ISO 639-1 code.
+
+ Returns:
+ A dict representation of the JSON returned from the API.
+ """
+ path = self._get_id_path('videos')
+
+ response = self._GET(path, kwargs)
+ self._set_attrs_to_values(response)
+ return response
+
+ def watch_providers(self, **kwargs):
+ """
+ Get a list of the availabilities per country by provider for movies.
+
+ Args:
+ None
+
+ Returns:
+ A dict representation of the JSON returned from the API.
+ """
+ path = self._get_id_path('watch_providers')
+
+ response = self._GET(path, kwargs)
+ self._set_attrs_to_values(response)
+ return response
+
+ def rating(self, **kwargs):
+ """
+ Rate a movie.
+
+ A valid session or guest session ID is required. You can read more
+ about how this works at
+ https://developers.themoviedb.org/3/authentication/how-do-i-generate-a-session-id.
+
+ Args:
+ session_id: (optional) See Authentication.
+ guest_session_id: (optional) See Authentication.
+ value: (required) This is the value of the rating you want to
+ submit. The value is expected to be between 0.5 and 10.0.
+
+ Returns:
+ A dict representation of the JSON returned from the API.
+ """
+ path = self._get_id_path('rating')
+
+ payload = {
+ 'value': kwargs.pop('value', None),
+ }
+
+ response = self._POST(path, kwargs, payload)
+ self._set_attrs_to_values(response)
+ return response
+
+ def rating_delete(self, **kwargs):
+ """
+ Remove your rating for a movie.
+
+ A valid session or guest session ID is required. You can read more
+ about how this works at
+ https://developers.themoviedb.org/3/authentication/how-do-i-generate-a-session-id.
+
+ Args:
+ session_id: (optional) See Authentication.
+ guest_session_id: (optional) See Authentication.
+
+ Returns:
+ A dict representation of the JSON returned from the API.
+ """
+ path = self._get_id_path('rating_delete')
+
+ payload = {
+ 'value': kwargs.pop('value', None),
+ }
+
+ response = self._DELETE(path, kwargs, payload)
+ self._set_attrs_to_values(response)
+ return response
+
+ def latest(self, **kwargs):
+ """
+ Get the most newly created movie. This is a live response and will
+ continuously change.
+
+ Args:
+ language: (optional) ISO 639-1 code.
+
+ Returns:
+ A dict representation of the JSON returned from the API.
+ """
+ path = self._get_path('latest')
+
+ response = self._GET(path, kwargs)
+ self._set_attrs_to_values(response)
+ return response
+
+ def now_playing(self, **kwargs):
+ """
+ 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 prameter which will narrow the
+ search to only look for theatrical release dates within the specified
+ country.
+
+ Args:
+ language: (optional) ISO 639-1 code.
+ page: (optional) Minimum 1, maximum 1000, default 1.
+ region: (optional) Specify a ISO 3166-1 code to filter release
+ dates. Must be uppercase.
+
+ Returns:
+ A dict representation of the JSON returned from the API.
+ """
+ path = self._get_path('now_playing')
+
+ response = self._GET(path, kwargs)
+ self._set_attrs_to_values(response)
+ return response
+
+ def popular(self, **kwargs):
+ """
+ Get a list of the current popular movies on TMDb. This list updates
+ daily.
+
+ Args:
+ language: (optional) ISO 639-1 code.
+ page: (optional) Minimum 1, maximum 1000, default 1.
+ region: (optional) Specify a ISO 3166-1 code to filter release
+ dates. Must be uppercase.
+
+ Returns:
+ A dict representation of the JSON returned from the API.
+ """
+ path = self._get_path('popular')
+
+ response = self._GET(path, kwargs)
+ self._set_attrs_to_values(response)
+ return response
+
+ def top_rated(self, **kwargs):
+ """
+ Get the top rated movies on TMDb.
+
+ Args:
+ language: (optional) ISO 639-1 code.
+ page: (optional) Minimum 1, maximum 1000, default 1.
+ region: (optional) Specify a ISO 3166-1 code to filter release
+ dates. Must be uppercase.
+
+ Returns:
+ A dict representation of the JSON returned from the API.
+ """
+ path = self._get_path('top_rated')
+
+ response = self._GET(path, kwargs)
+ self._set_attrs_to_values(response)
+ return response
+
+ def upcoming(self, **kwargs):
+ """
+ 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 prameter which will narrow the
+ search to only look for theatrical release dates within the specified
+ country.
+
+ Args:
+ language: (optional) ISO 639-1 code.
+ page: (optional) Minimum 1, maximum 1000, default 1.
+ region: (optional) Specify a ISO 3166-1 code to filter release
+ dates. Must be uppercase.
+
+ Returns:
+ A dict representation of the JSON returned from the API.
+ """
+ path = self._get_path('upcoming')
+
+ response = self._GET(path, kwargs)
+ self._set_attrs_to_values(response)
+ return response
+
+ # backward compatability
+ def releases(self, **kwargs):
+ """
+ Get the release date and certification information by country for a
+ specific movie id.
+
+ Args:
+ None
+
+ Returns:
+ A dict representation of the JSON returned from the API.
+ """
+ path = self._get_id_path('releases')
+
+ response = self._GET(path, kwargs)
+ self._set_attrs_to_values(response)
+ return response
+
+
+class Collections(TMDB):
+ """
+ Collections functionality.
+
+ See: https://developers.themoviedb.org/3/collections
+ """
+ BASE_PATH = 'collection'
+ URLS = {
+ 'info': '/{id}',
+ 'images': '/{id}/images',
+ 'translations': '/{id}/translations',
+ }
+
+ def __init__(self, id):
+ super(Collections, self).__init__()
+ self.id = id
+
+ def info(self, **kwargs):
+ """
+ Get collection details by id.
+
+ Args:
+ language: (optional) ISO 639-1 code.
+
+ Returns:
+ A dict representation of the JSON returned from the API.
+ """
+ path = self._get_id_path('info')
+
+ response = self._GET(path, kwargs)
+ self._set_attrs_to_values(response)
+ return response
+
+ def images(self, **kwargs):
+ """
+ Get the images for a collection by id.
+
+ Args:
+ language: (optional) ISO 639-1 code.
+
+ Returns:
+ A dict representation of the JSON returned from the API.
+ """
+ path = self._get_id_path('images')
+
+ response = self._GET(path, kwargs)
+ self._set_attrs_to_values(response)
+ return response
+
+ def translations(self, **kwargs):
+ """
+ Get a list of the translations for a collection by id.
+
+ Args:
+ language: (optional) ISO 639-1 code.
+
+ Returns:
+ A dict representation of the JSON returned from the API.
+ """
+ path = self._get_id_path('translations')
+
+ response = self._GET(path, kwargs)
+ self._set_attrs_to_values(response)
+ return response
+
+
+class Companies(TMDB):
+ """
+ Companies functionality.
+
+ See: https://developers.themoviedb.org/3/companies
+ """
+ BASE_PATH = 'company'
+ URLS = {
+ 'info': '/{id}',
+ 'alternative_names': '/{id}/alternative_names',
+ 'images': '/{id}/images',
+ 'movies': '/{id}/movies', # backward compatability
+ }
+
+ def __init__(self, id=0):
+ super(Companies, self).__init__()
+ self.id = id
+
+ def info(self, **kwargs):
+ """
+ Get a companies details by id.
+
+ Args:
+
+ Returns:
+ A dict representation of the JSON returned from the API.
+ """
+ path = self._get_id_path('info')
+
+ response = self._GET(path, kwargs)
+ self._set_attrs_to_values(response)
+ return response
+
+ def alternative_names(self, **kwargs):
+ """
+ Get the alternative names of a company.
+
+ Args:
+
+ Returns:
+ A dict representation of the JSON returned from the API.
+ """
+ path = self._get_id_path('alternative_names')
+
+ response = self._GET(path, kwargs)
+ self._set_attrs_to_values(response)
+ return response
+
+ def images(self, **kwargs):
+ """
+ Get a company's 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 https://developers.themoviedb.org/3/getting-started/images.
+
+ Args:
+
+ Returns:
+ A dict representation of the JSON returned from the API.
+ """
+ path = self._get_id_path('images')
+
+ response = self._GET(path, kwargs)
+ self._set_attrs_to_values(response)
+ return response
+
+ # backward compatability
+ def movies(self, **kwargs):
+ """
+ Get the list of movies associated with a particular company.
+
+ Args:
+ language: (optional) ISO 639-1 code.
+ page: (optional) Minimum value of 1. Expected value is an integer.
+
+ Returns:
+ A dict representation of the JSON returned from the API.
+ """
+ path = self._get_id_path('movies')
+
+ response = self._GET(path, kwargs)
+ self._set_attrs_to_values(response)
+ return response
+
+
+class Keywords(TMDB):
+ """
+ Keywords functionality.
+
+ See: https://developers.themoviedb.org/3/keywords
+ """
+ BASE_PATH = 'keyword'
+ URLS = {
+ 'info': '/{id}',
+ 'movies': '/{id}/movies',
+ }
+
+ def __init__(self, id):
+ super(Keywords, self).__init__()
+ self.id = id
+
+ def info(self, **kwargs):
+ """
+ Get the details of a keyword.
+
+ Args:
+ None
+
+ Returns:
+ A dict representation of the JSON returned from the API.
+ """
+ path = self._get_id_path('info')
+
+ response = self._GET(path, kwargs)
+ self._set_attrs_to_values(response)
+ return response
+
+ def movies(self, **kwargs):
+ """
+ Get the movies that belong to a keyword.
+
+ We highly recommend using movie discover instead of this method as it
+ is much more flexible.
+
+ Args:
+ language: (optional) ISO 639-1 code.
+ include_adult: Choose whether to inlcude adult (pornography)
+ content in the results.
+
+ Returns:
+ A dict representation of the JSON returned from the API.
+ """
+ path = self._get_id_path('movies')
+
+ response = self._GET(path, kwargs)
+ self._set_attrs_to_values(response)
+ return response
+
+
+class Reviews(TMDB):
+ """
+ Reviews functionality.
+
+ See: https://developers.themoviedb.org/3/reviews
+ """
+ BASE_PATH = 'review'
+ URLS = {
+ 'info': '/{id}',
+ }
+
+ def __init__(self, id):
+ super(Reviews, self).__init__()
+ self.id = id
+
+ def info(self, **kwargs):
+ """
+ Get the review details by id.
+
+ Args:
+ None
+
+ Returns:
+ A dict representation of the JSON returned from the API.
+ """
+ path = self._get_id_path('info')
+
+ response = self._GET(path, kwargs)
+ self._set_attrs_to_values(response)
+ return response
diff --git a/src/tmdbsimple/people.py b/src/tmdbsimple/people.py
new file mode 100644
index 0000000..acceed7
--- /dev/null
+++ b/src/tmdbsimple/people.py
@@ -0,0 +1,275 @@
+# -*- coding: utf-8 -*-
+
+"""
+tmdbsimple.people
+~~~~~~~~~~~~~~~~~
+This module implements the People and Credits functionality of tmdbsimple.
+
+Created by Celia Oakley on 2013-10-31.
+
+:copyright: (c) 2013-2022 by Celia Oakley
+:license: GPLv3, see LICENSE for more details
+"""
+
+from .base import TMDB
+
+
+class People(TMDB):
+ """
+ People functionality.
+
+ See: https://developers.themoviedb.org/3/people
+ """
+ BASE_PATH = 'person'
+ URLS = {
+ 'info': '/{id}',
+ 'changes': '/{id}/changes',
+ 'movie_credits': '/{id}/movie_credits',
+ 'tv_credits': '/{id}/tv_credits',
+ 'combined_credits': '/{id}/combined_credits',
+ 'external_ids': '/{id}/external_ids',
+ 'images': '/{id}/images',
+ 'tagged_images': '/{id}/tagged_images',
+ 'translations': '/{id}/translations',
+ 'latest': '/latest',
+ 'popular': '/popular',
+ }
+
+ def __init__(self, id=0):
+ super(People, self).__init__()
+ self.id = id
+
+ def info(self, **kwargs):
+ """
+ Get the primary person details by id.
+
+ Supports append_to_response. Read more about this at
+ https://developers.themoviedb.org/3/getting-started/append-to-response.
+
+ Args:
+ language: (optional) ISO 639-1 code.
+ append_to_response: (optional) Append requests within the same
+ namespace to the response.
+
+ Returns:
+ A dict respresentation of the JSON returned from the API.
+ """
+ path = self._get_id_path('info')
+
+ response = self._GET(path, kwargs)
+ self._set_attrs_to_values(response)
+ return response
+
+ def changes(self, **kwargs):
+ """
+ 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.
+
+ Args:
+ start_date: (optional) Filter the results with a start date.
+ Expected format is 'YYYY-MM-DD'.
+ end_date: (optional) Filter the results with a end date.
+ Expected format is 'YYYY-MM-DD'.
+ page: (optional) Minimum 1, maximum 1000, default 1.
+
+ Returns:
+ A dict respresentation of the JSON returned from the API.
+ """
+ path = self._get_id_path('changes')
+
+ response = self._GET(path, kwargs)
+ self._set_attrs_to_values(response)
+ return response
+
+ def movie_credits(self, **kwargs):
+ """
+ Get the movie credits for a person.
+
+ Args:
+ language: (optional) ISO 639-1 code.
+
+ Returns:
+ A dict respresentation of the JSON returned from the API.
+ """
+ path = self._get_id_path('movie_credits')
+
+ response = self._GET(path, kwargs)
+ self._set_attrs_to_values(response)
+ return response
+
+ def tv_credits(self, **kwargs):
+ """
+ Get the TV show credits for a person.
+
+ You can query for some extra details about the credit with the credit
+ method.
+
+ Args:
+ language: (optional) ISO 639-1 code.
+
+ Returns:
+ A dict respresentation of the JSON returned from the API.
+ """
+ path = self._get_id_path('tv_credits')
+
+ response = self._GET(path, kwargs)
+ self._set_attrs_to_values(response)
+ return response
+
+ def combined_credits(self, **kwargs):
+ """
+ Get the movie and TV credits together in a single response.
+
+ Args:
+ language: (optional) ISO 639-1 code.
+
+ Returns:
+ A dict respresentation of the JSON returned from the API.
+ """
+ path = self._get_id_path('combined_credits')
+
+ response = self._GET(path, kwargs)
+ self._set_attrs_to_values(response)
+ return response
+
+ def external_ids(self, **kwargs):
+ """
+ Get the external ids for a person. We currently support the following external sources.
+
+ External Sources
+ - IMDB ID
+ - Facebook
+ - Freebase MID
+ - Freebase ID
+ - Instagram
+ - TVRage ID
+ - Twitter
+
+ Args:
+ language: (optional) ISO 639-1 code.
+
+ Returns:
+ A dict respresentation of the JSON returned from the API.
+ """
+ path = self._get_id_path('external_ids')
+
+ response = self._GET(path, kwargs)
+ self._set_attrs_to_values(response)
+ return response
+
+ def images(self, **kwargs):
+ """
+ Get the images for a person.
+
+ Args:
+ None
+
+ Returns:
+ A dict respresentation of the JSON returned from the API.
+ """
+ path = self._get_id_path('images')
+
+ response = self._GET(path, kwargs)
+ self._set_attrs_to_values(response)
+ return response
+
+ def tagged_images(self, **kwargs):
+ """
+ Get the images that this person has been tagged in.
+
+ Args:
+ language: (optional) ISO 639-1 code.
+ page: (optional) Minimum 1, maximum 1000, default 1.
+
+ Returns:
+ A dict respresentation of the JSON returned from the API.
+ """
+ path = self._get_id_path('tagged_images')
+
+ response = self._GET(path, kwargs)
+ self._set_attrs_to_values(response)
+ return response
+
+ def translations(self, **kwargs):
+ """
+ Get a list of translations that have been created for a person.
+
+ Args:
+ language: (optional) ISO 639-1 code.
+
+ Returns:
+ A dict respresentation of the JSON returned from the API.
+ """
+ path = self._get_id_path('translations')
+
+ response = self._GET(path, kwargs)
+ self._set_attrs_to_values(response)
+ return response
+
+ def latest(self, **kwargs):
+ """
+ Get the most newly created person. This is a live response and will
+ continuously change.
+
+ Args:
+ language: (optional) ISO 639-1 code.
+
+ Returns:
+ A dict respresentation of the JSON returned from the API.
+ """
+ path = self._get_path('latest')
+
+ response = self._GET(path, kwargs)
+ self._set_attrs_to_values(response)
+ return response
+
+ def popular(self, **kwargs):
+ """
+ Get the list of popular people on TMDb. This list updates daily.
+
+ Args:
+ language: (optional) ISO 639-1 code.
+ page: (optional) Minimum 1, maximum 1000, default 1.
+
+ Returns:
+ A dict respresentation of the JSON returned from the API.
+ """
+ path = self._get_path('popular')
+
+ response = self._GET(path, kwargs)
+ self._set_attrs_to_values(response)
+ return response
+
+
+class Credits(TMDB):
+ """
+ Credits functionality.
+
+ See: https://developers.themoviedb.org/3/credits
+ """
+ BASE_PATH = 'credit'
+ URLS = {
+ 'info': '/{credit_id}',
+ }
+
+ def __init__(self, credit_id):
+ super(Credits, self).__init__()
+ self.credit_id = credit_id
+
+ def info(self, **kwargs):
+ """
+ Get a movie or TV credit details by id.
+
+ Args:
+ None
+
+ Returns:
+ A dict respresentation of the JSON returned from the API.
+ """
+ path = self._get_credit_id_path('info')
+
+ response = self._GET(path, kwargs)
+ self._set_attrs_to_values(response)
+ return response
diff --git a/src/tmdbsimple/search.py b/src/tmdbsimple/search.py
new file mode 100644
index 0000000..6b10c2e
--- /dev/null
+++ b/src/tmdbsimple/search.py
@@ -0,0 +1,184 @@
+# -*- coding: utf-8 -*-
+
+"""
+tmdbsimple.search
+~~~~~~~~~~~~~~~~~
+This module implements the Search functionality of tmdbsimple.
+
+Created by Celia Oakley on 2013-10-31.
+
+:copyright: (c) 2013-2022 by Celia Oakley
+:license: GPLv3, see LICENSE for more details
+"""
+
+from .base import TMDB
+
+
+class Search(TMDB):
+ """
+ Search functionality
+
+ See: https://developers.themoviedb.org/3/search
+ """
+ BASE_PATH = 'search'
+ URLS = {
+ 'company': '/company',
+ 'collection': '/collection',
+ 'keyword': '/keyword',
+ 'movie': '/movie',
+ 'multi': '/multi',
+ 'person': '/person',
+ 'tv': '/tv',
+ }
+
+ def company(self, **kwargs):
+ """
+ Search for companies.
+
+ Args:
+ query: (required) Pass a text query to search. This value should be
+ URI encoded.
+ page: (optional) Minimum 1, maximum 1000, default 1.
+
+ Returns:
+ A dict respresentation of the JSON returned from the API.
+ """
+ path = self._get_path('company')
+
+ response = self._GET(path, kwargs)
+ self._set_attrs_to_values(response)
+ return response
+
+ def collection(self, **kwargs):
+ """
+ Search for collections.
+
+ Args:
+ language: (optional) (optional) ISO 639-1 code.
+ query: (required) Pass a text query to search. This value should be
+ URI encoded.
+ page: (optional) Minimum 1, maximum 1000, default 1.
+
+ Returns:
+ A dict respresentation of the JSON returned from the API.
+ """
+ path = self._get_path('collection')
+
+ response = self._GET(path, kwargs)
+ self._set_attrs_to_values(response)
+ return response
+
+ def keyword(self, **kwargs):
+ """
+ Search for keywords.
+
+ Args:
+ query: (required) Pass a text query to search. This value should be
+ URI encoded.
+ page: (optional) Minimum 1, maximum 1000, default 1.
+
+ Returns:
+ A dict respresentation of the JSON returned from the API.
+ """
+ path = self._get_path('keyword')
+
+ response = self._GET(path, kwargs)
+ self._set_attrs_to_values(response)
+ return response
+
+ def movie(self, **kwargs):
+ """
+ Search for movies.
+
+ Args:
+ language: (optional) (optional) ISO 639-1 code.
+ query: (required) Pass a text query to search. This value should be
+ URI encoded.
+ page: (optional) Minimum 1, maximum 1000, default 1.
+ include_adult: (optional) Choose whether to inlcude adult
+ (pornography) content in the results.
+ region: (optional) Specify a ISO 3166-1 code to filter release
+ dates. Must be uppercase.
+ year: (optional) A filter to limit the results to a specific year
+ (looking at all release dates).
+ primary_release_year: (optional) A filter to limit the results to a
+ specific primary release year.
+
+ Returns:
+ A dict respresentation of the JSON returned from the API.
+ """
+ path = self._get_path('movie')
+
+ response = self._GET(path, kwargs)
+ self._set_attrs_to_values(response)
+ return response
+
+ def multi(self, **kwargs):
+ """
+ Search multiple models in a single request. Multi search currently
+ supports searching for movies, tv shows and people in a single request.
+
+ Args:
+ language: (optional) (optional) ISO 639-1 code.
+ query: (required) Pass a text query to search. This value should be
+ URI encoded.
+ page: (optional) Minimum 1, maximum 1000, default 1.
+ include_adult: (optional) Choose whether to inlcude adult
+ (pornography) content in the results.
+ region: (optional) Specify a ISO 3166-1 code to filter release
+ dates. Must be uppercase.
+
+ Returns:
+ A dict respresentation of the JSON returned from the API.
+ """
+ path = self._get_path('multi')
+
+ response = self._GET(path, kwargs)
+ self._set_attrs_to_values(response)
+ return response
+
+ def person(self, **kwargs):
+ """
+ Search for people.
+
+ Args:
+ language: (optional) (optional) ISO 639-1 code.
+ query: (required) Pass a text query to search. This value should be
+ URI encoded.
+ page: (optional) Minimum 1, maximum 1000, default 1.
+ include_adult: (optional) Choose whether to inlcude adult
+ (pornography) content in the results.
+ region: (optional) Specify a ISO 3166-1 code to filter release
+ dates. Must be uppercase.
+
+ Returns:
+ A dict respresentation of the JSON returned from the API.
+ """
+ path = self._get_path('person')
+
+ response = self._GET(path, kwargs)
+ self._set_attrs_to_values(response)
+ return response
+
+ def tv(self, **kwargs):
+ """
+ Search for a TV show.
+
+ Args:
+ language: (optional) (optional) ISO 639-1 code.
+ query: (required) Pass a text query to search. This value should be
+ URI encoded.
+ page: (optional) Minimum 1, maximum 1000, default 1.
+ include_adult: (optional) Choose whether to inlcude adult
+ (pornography) content in the results.
+ first_air_date_year: (optional) Filter the results to only match
+ shows that have an air date with with value.
+
+ Returns:
+ A dict respresentation of the JSON returned from the API.
+ """
+ path = self._get_path('tv')
+
+ response = self._GET(path, kwargs)
+ self._set_attrs_to_values(response)
+ return response
diff --git a/src/tmdbsimple/tv.py b/src/tmdbsimple/tv.py
new file mode 100644
index 0000000..4f1b41f
--- /dev/null
+++ b/src/tmdbsimple/tv.py
@@ -0,0 +1,1040 @@
+# -*- coding: utf-8 -*-
+
+"""
+tmdbsimple.tv
+~~~~~~~~~~~~~
+This module implements the TV, TV Seasons, TV Episodes, and Networks
+functionality of tmdbsimple.
+
+Created by Celia Oakley on 2013-10-31.
+
+:copyright: (c) 2013-2022 by Celia Oakley
+:license: GPLv3, see LICENSE for more details
+"""
+
+from .base import TMDB
+
+
+class TV(TMDB):
+ """
+ TV functionality.
+
+ See: https://developers.themoviedb.org/3/tv
+ """
+ BASE_PATH = 'tv'
+ URLS = {
+ 'info': '/{id}',
+ 'account_states': '/{id}/account_states',
+ 'alternative_titles': '/{id}/alternative_titles',
+ 'content_ratings': '/{id}/content_ratings',
+ 'credits': '/{id}/credits',
+ 'episode_groups': '/{id}/episode_groups',
+ 'external_ids': '/{id}/external_ids',
+ 'images': '/{id}/images',
+ 'keywords': '/{id}/keywords',
+ 'recommendations': '/{id}/recommendations',
+ 'reviews': '/{id}/reviews',
+ 'screened_theatrically': '/{id}/screened_theatrically',
+ 'similar': '/{id}/similar',
+ 'translations': '/{id}/translations',
+ 'videos': '/{id}/videos',
+ 'watch_providers': '/{id}/watch/providers',
+ 'rating': '/{id}/rating',
+ 'latest': '/latest',
+ 'airing_today': '/airing_today',
+ 'on_the_air': '/on_the_air',
+ 'popular': '/popular',
+ 'top_rated': '/top_rated',
+ }
+
+ def __init__(self, id=0):
+ super(TV, self).__init__()
+ self.id = id
+
+ def info(self, **kwargs):
+ """
+ Get the primary TV show details by id.
+
+ Supports append_to_response. Read more about this at
+ https://developers.themoviedb.org/3/getting-started/append-to-response.
+
+ Args:
+ language: (optional) ISO 639 code.
+ append_to_response: (optional) Append requests within the same
+ namespace to the response.
+
+ Returns:
+ A dict respresentation of the JSON returned from the API.
+ """
+ path = self._get_id_path('info')
+
+ response = self._GET(path, kwargs)
+ self._set_attrs_to_values(response)
+ return response
+
+ def account_states(self, **kwargs):
+ """
+ Grab the following account states for a session:
+ - TV show rating
+ - If it belongs to your watchlist
+ - If it belongs to your favourite list
+
+ Args:
+ language: (optional) ISO 3166-1 code.
+ session_id: (required) See Authentication.
+ guest_session_id: (optional) See Authentication.
+
+ Returns:
+ A dict respresentation of the JSON returned from the API.
+ """
+ path = self._get_id_path('account_states')
+
+ response = self._GET(path, kwargs)
+ self._set_attrs_to_values(response)
+ return response
+
+ def alternative_titles(self, **kwargs):
+ """
+ Returns all of the alternative titles for a TV show.
+
+ Args:
+ language: (optional) ISO 3166-1 code.
+
+ Returns:
+ A dict respresentation of the JSON returned from the API.
+ """
+ path = self._get_id_path('alternative_titles')
+
+ response = self._GET(path, kwargs)
+ self._set_attrs_to_values(response)
+ return response
+
+ def content_ratings(self, **kwargs):
+ """
+ Get the list of content ratings (certifications) that have been added
+ to a TV show.
+
+ Args:
+ language: (optional) ISO 3166-1 code.
+
+ Returns:
+ A dict respresentation of the JSON returned from the API.
+ """
+ path = self._get_id_path('content_ratings')
+
+ response = self._GET(path, kwargs)
+ self._set_attrs_to_values(response)
+ return response
+
+ def credits(self, **kwargs):
+ """
+ Get the credits (cast and crew) that have been added to a TV show.
+
+ Args:
+ language: (optional) ISO 639 code.
+
+ Returns:
+ A dict respresentation of the JSON returned from the API.
+ """
+ path = self._get_id_path('credits')
+
+ response = self._GET(path, kwargs)
+ self._set_attrs_to_values(response)
+ return response
+
+ def episode_groups(self, **kwargs):
+ """
+ Get all of the episode groups that have been created for a TV show.
+ With a group ID you can call the get TV episode group details method.
+
+ Args:
+ language: (optional) ISO 639 code.
+
+ Returns:
+ A dict respresentation of the JSON returned from the API.
+ """
+ path = self._get_id_path('episode_groups')
+
+ response = self._GET(path, kwargs)
+ self._set_attrs_to_values(response)
+ return response
+
+ def external_ids(self, **kwargs):
+ """
+ Get the external ids for a TV show. We currently support the following
+ external sources.
+
+ Media Databases: IMDb ID, TVDB ID, Freebase MID*, Freebase ID*, TVRage
+ ID*
+ Social IDs: Facebook, Instagram, Twitter
+
+ *Defunct or no longer available as a service.
+
+ Args:
+ language: (optional) ISO 639 code.
+
+ Returns:
+ A dict respresentation of the JSON returned from the API.
+ """
+ path = self._get_id_path('external_ids')
+
+ response = self._GET(path, kwargs)
+ self._set_attrs_to_values(response)
+ return response
+
+ def images(self, **kwargs):
+ """
+ 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 seperated value like so:
+ include_image_language=en,null.
+
+ Args:
+ language: (optional) ISO 639 code.
+
+ Returns:
+ A dict respresentation of the JSON returned from the API.
+ """
+ path = self._get_id_path('images')
+
+ response = self._GET(path, kwargs)
+ self._set_attrs_to_values(response)
+ return response
+
+ def keywords(self, **kwargs):
+ """
+ Get the keywords that have been added to a TV show.
+
+ Args:
+ None
+
+ Returns:
+ A dict respresentation of the JSON returned from the API.
+ """
+ path = self._get_id_path('keywords')
+
+ response = self._GET(path, kwargs)
+ self._set_attrs_to_values(response)
+ return response
+
+ def recommendations(self, **kwargs):
+ """
+ Get the list of TV show recommendations for this item.
+
+ Args:
+ language: (optional) ISO 639-1 code.
+ page: (optional) Minimum 1, maximum 1000, default 1.
+
+ Returns:
+ A dict respresentation of the JSON returned from the API.
+ """
+ path = self._get_id_path('recommendations')
+
+ response = self._GET(path, kwargs)
+ self._set_attrs_to_values(response)
+ return response
+
+ def reviews(self, **kwargs):
+ """
+ Get the reviews for a TV show.
+
+ Args:
+ language: (optional) ISO 639-1 code.
+ page: (optional) Minimum 1, maximum 1000, default 1.
+
+ Returns:
+ A dict respresentation of the JSON returned from the API.
+ """
+ path = self._get_id_path('reviews')
+
+ response = self._GET(path, kwargs)
+ self._set_attrs_to_values(response)
+ return response
+
+ def screened_theatrically(self, **kwargs):
+ """
+ Get a list of seasons or episodes that have been screened in a film
+ festival or theatre.
+
+ Args:
+ None
+
+ Returns:
+ A dict respresentation of the JSON returned from the API.
+ """
+ path = self._get_id_path('screened_theatrically')
+
+ response = self._GET(path, kwargs)
+ self._set_attrs_to_values(response)
+ return response
+
+ def similar(self, **kwargs):
+ """
+ Get a list of similar TV shows. These items are assembled by looking at
+ keywords and genres.
+
+ Args:
+ language: (optional) ISO 639-1 code.
+ page: (optional) Minimum 1, maximum 1000, default 1.
+
+ Returns:
+ A dict respresentation of the JSON returned from the API.
+ """
+ path = self._get_id_path('similar')
+
+ response = self._GET(path, kwargs)
+ self._set_attrs_to_values(response)
+ return response
+
+ def translations(self, **kwargs):
+ """
+ Get a list of the translations that exist for a TV show.
+
+ Args:
+ None
+
+ Returns:
+ A dict respresentation of the JSON returned from the API.
+ """
+ path = self._get_id_path('translations')
+
+ response = self._GET(path, kwargs)
+ self._set_attrs_to_values(response)
+ return response
+
+ def videos(self, **kwargs):
+ """
+ Get the videos that have been added to a TV show.
+
+ Args:
+ language: (optional) ISO 639 code.
+
+ Returns:
+ A dict respresentation of the JSON returned from the API.
+ """
+ path = self._get_id_path('videos')
+
+ response = self._GET(path, kwargs)
+ self._set_attrs_to_values(response)
+ return response
+
+ def watch_providers(self, **kwargs):
+ """
+ Get a list of the availabilities per country by provider for tv.
+
+ Args:
+ None
+
+ Returns:
+ A dict respresentation of the JSON returned from the API.
+ """
+ path = self._get_id_path('watch_providers')
+
+ response = self._GET(path, kwargs)
+ self._set_attrs_to_values(response)
+
+ def rating(self, **kwargs):
+ """
+ Rate a TV show.
+
+ A valid session or guest session ID is required. You can read more
+ about how this works at
+ https://developers.themoviedb.org/3/authentication/how-do-i-generate-a-session-id.
+
+ Args:
+ session_id: (optional) See Authentication.
+ guest_session_id: (optional) See Authentication.
+ value: (required) This is the value of the rating you want to
+ submit. The value is expected to be between 0.5 and 10.0.
+
+ Returns:
+ A dict respresentation of the JSON returned from the API.
+ """
+ path = self._get_id_path('rating')
+
+ payload = {
+ 'value': kwargs.pop('value', None),
+ }
+
+ response = self._POST(path, kwargs, payload)
+ self._set_attrs_to_values(response)
+ return response
+
+ def rating_delete(self, **kwargs):
+ """
+ Remove your rating for a TV show.
+
+ A valid session or guest session ID is required. You can read more
+ about how this works at
+ https://developers.themoviedb.org/3/authentication/how-do-i-generate-a-session-id.
+
+ Args:
+ session_id: (optional) See Authentication.
+ guest_session_id: (optional) See Authentication.
+
+ Returns:
+ A dict respresentation of the JSON returned from the API.
+ """
+ path = self._get_id_path('rating')
+
+ payload = {
+ 'value': kwargs.pop('value', None),
+ }
+
+ response = self._DELETE(path, kwargs, payload)
+ self._set_attrs_to_values(response)
+ return response
+
+ def latest(self, **kwargs):
+ """
+ Get the most newly created TV show. This is a live response and will
+ continuously change.
+
+ Args:
+ language: (optional) ISO 639 code.
+
+ Returns:
+ A dict respresentation of the JSON returned from the API.
+ """
+ path = self._get_id_path('latest')
+
+ response = self._GET(path, kwargs)
+ self._set_attrs_to_values(response)
+ return response
+
+ def airing_today(self, **kwargs):
+ """
+ Get a list of TV shows that are airing today. This query is purely day
+ based as we do not currently support airing times.
+
+ You can specify a timezone to offset the day calculation. Without a
+ specified timezone, this query defaults to EST (Eastern Time
+ UTC-05:00).
+
+ Args:
+ language: (optional) ISO 639 code.
+ page: (optional) Minimum 1, maximum 1000, default 1.
+
+ Returns:
+ A dict respresentation of the JSON returned from the API.
+ """
+ path = self._get_path('airing_today')
+
+ response = self._GET(path, kwargs)
+ self._set_attrs_to_values(response)
+ return response
+
+ def on_the_air(self, **kwargs):
+ """
+ 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.
+
+ Args:
+ language: (optional) ISO 639 code.
+ page: (optional) Minimum 1, maximum 1000, default 1.
+
+ Returns:
+ A dict respresentation of the JSON returned from the API.
+ """
+ path = self._get_path('on_the_air')
+
+ response = self._GET(path, kwargs)
+ self._set_attrs_to_values(response)
+ return response
+
+ def popular(self, **kwargs):
+ """
+ Get a list of the current popular TV shows on TMDb. This list updates
+ daily.
+
+ Args:
+ language: (optional) ISO 639 code.
+ page: (optional) Minimum 1, maximum 1000, default 1.
+
+ Returns:
+ A dict respresentation of the JSON returned from the API.
+ """
+ path = self._get_path('popular')
+
+ response = self._GET(path, kwargs)
+ self._set_attrs_to_values(response)
+ return response
+
+ def top_rated(self, **kwargs):
+ """
+ Get a list of the top rated TV shows on TMDb.
+
+ Args:
+ language: (optional) ISO 639 code.
+ page: (optional) Minimum 1, maximum 1000, default 1.
+
+ Returns:
+ A dict respresentation of the JSON returned from the API.
+ """
+ path = self._get_path('top_rated')
+
+ response = self._GET(path, kwargs)
+ self._set_attrs_to_values(response)
+ return response
+
+
+class TV_Seasons(TMDB):
+ """
+ TV Seasons functionality.
+
+ See: https://developers.themoviedb.org/3/tv-seasons
+ """
+ BASE_PATH = 'tv/{tv_id}/season/{season_number}'
+ URLS = {
+ 'info': '',
+ 'account_states': '/account_states',
+ 'credits': '/credits',
+ 'external_ids': '/external_ids',
+ 'images': '/images',
+ 'videos': '/videos',
+ }
+
+ def __init__(self, tv_id, season_number):
+ super(TV_Seasons, self).__init__()
+ self.tv_id = tv_id
+ self.season_number = season_number
+
+ def info(self, **kwargs):
+ """
+ Get the TV season details by id.
+
+ Supports append_to_response. Read more about this at
+ https://developers.themoviedb.org/3/getting-started/append-to-response.
+
+ Args:
+ language: (optional) ISO 639 code.
+ append_to_response: (optional) Append requests within the same
+ namespace to the response.
+
+ Returns:
+ A dict respresentation of the JSON returned from the API.
+ """
+ path = self._get_tv_id_season_number_path('info')
+
+ response = self._GET(path, kwargs)
+ self._set_attrs_to_values(response)
+ return response
+
+ def account_states(self, **kwargs):
+ """
+ Returns all of the user ratings for the season's episodes.
+
+ Args:
+ language: (optional) ISO 639 code.
+ session_id: (required) See Authentication.
+ guest_session_id: (optional) See Authentication.
+
+ Returns:
+ A dict respresentation of the JSON returned from the API.
+ """
+ path = self._get_tv_id_season_number_path('account_states')
+
+ response = self._GET(path, kwargs)
+ self._set_attrs_to_values(response)
+ return response
+
+ def credits(self, **kwargs):
+ """
+ Get the credits for TV season.
+
+ Args:
+ language: (optional) ISO 639 code.
+
+ Returns:
+ A dict respresentation of the JSON returned from the API.
+ """
+ path = self._get_tv_id_season_number_path('credits')
+
+ response = self._GET(path, kwargs)
+ self._set_attrs_to_values(response)
+ return response
+
+ def external_ids(self, **kwargs):
+ """
+ Get the external ids for a TV season. We currently support the
+ following external sources.
+
+ Media Databases: TVDB ID, Freebase MID*, Freebase ID*, TVRage ID*
+
+ *Defunct or no longer available as a service.
+
+ Args:
+ language: (optional) ISO 639 code.
+
+ Returns:
+ A dict respresentation of the JSON returned from the API.
+ """
+ path = self._get_tv_id_season_number_path('external_ids')
+
+ response = self._GET(path, kwargs)
+ self._set_attrs_to_values(response)
+ return response
+
+ def images(self, **kwargs):
+ """
+ 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 seperated value like so:
+ include_image_language=en,null.
+
+ Args:
+ language: (optional) ISO 639 code.
+
+ Returns:
+ A dict respresentation of the JSON returned from the API.
+ """
+ path = self._get_tv_id_season_number_path('images')
+
+ response = self._GET(path, kwargs)
+ self._set_attrs_to_values(response)
+ return response
+
+ def videos(self, **kwargs):
+ """
+ Get the videos that have been added to a TV season.
+
+ Args:
+ language: (optional) ISO 639 code.
+
+ Returns:
+ A dict respresentation of the JSON returned from the API.
+ """
+ path = self._get_tv_id_season_number_path('videos')
+
+ response = self._GET(path, kwargs)
+ self._set_attrs_to_values(response)
+ return response
+
+
+class TV_Episodes(TMDB):
+ """
+ TV Episodes functionality.
+
+ See: https://developers.themoviedb.org/3/tv-episodes
+ """
+ BASE_PATH = 'tv/{tv_id}/season/{season_number}/episode/{episode_number}'
+ URLS = {
+ 'info': '',
+ 'account_states': '/account_states',
+ 'credits': '/credits',
+ 'external_ids': '/external_ids',
+ 'images': '/images',
+ 'translations': '/translations',
+ 'rating': '/rating',
+ 'videos': '/videos',
+ }
+
+ def __init__(self, tv_id, season_number, episode_number):
+ super(TV_Episodes, self).__init__()
+ self.tv_id = tv_id
+ self.season_number = season_number
+ self.episode_number = episode_number
+
+ def info(self, **kwargs):
+ """
+ Get the TV episode details by id.
+
+ Supports append_to_response. Read more about this at
+ https://developers.themoviedb.org/3/getting-started/append-to-response.
+
+ Args:
+ language: (optional) ISO 639 code.
+ append_to_response: (optional) Append requests within the same
+ namespace to the response.
+
+ Returns:
+ A dict respresentation of the JSON returned from the API.
+ """
+ path = self._get_tv_id_season_number_episode_number_path('info')
+
+ response = self._GET(path, kwargs)
+ self._set_attrs_to_values(response)
+ return response
+
+ def account_states(self, **kwargs):
+ """
+ Get your rating for an episode.
+
+ Args:
+ session_id: (required) See Authentication.
+ guest_session_id: (optional) See Authentication.
+
+ Returns:
+ A dict respresentation of the JSON returned from the API.
+ """
+ path = self._get_tv_id_season_number_episode_number_path(
+ 'account_states')
+
+ response = self._GET(path, kwargs)
+ self._set_attrs_to_values(response)
+ return response
+
+ def credits(self, **kwargs):
+ """
+ Get the credits (cast, crew and guest stars) for a TV episode.
+
+ Args:
+ None
+
+ Returns:
+ A dict respresentation of the JSON returned from the API.
+ """
+ path = self._get_tv_id_season_number_episode_number_path('credits')
+
+ response = self._GET(path, kwargs)
+ self._set_attrs_to_values(response)
+ return response
+
+ def external_ids(self, **kwargs):
+ """
+ Get the external ids for a TV episode. We currently support the
+ following external sources.
+
+ External Sources: IMDb ID, TVDB ID, Freebase MID*, Freebase ID*, TVRage
+ ID*
+
+ *Defunct or no longer available as a service.
+
+ Args:
+ None
+
+ Returns:
+ A dict respresentation of the JSON returned from the API.
+ """
+ path = self._get_tv_id_season_number_episode_number_path(
+ 'external_ids')
+
+ response = self._GET(path, kwargs)
+ self._set_attrs_to_values(response)
+ return response
+
+ def images(self, **kwargs):
+ """
+ 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 seperated value like so:
+ include_image_language=en,null.
+
+ Args:
+ None
+
+ Returns:
+ A dict respresentation of the JSON returned from the API.
+ """
+ path = self._get_tv_id_season_number_episode_number_path('images')
+
+ response = self._GET(path, kwargs)
+ self._set_attrs_to_values(response)
+ return response
+
+ def translations(self, **kwargs):
+ """
+ Get the translation data for an episode.
+
+ Args:
+ None
+
+ Returns:
+ A dict respresentation of the JSON returned from the API.
+ """
+ path = self._get_tv_id_season_number_episode_number_path(
+ 'translations')
+
+ response = self._GET(path, kwargs)
+ self._set_attrs_to_values(response)
+ return response
+
+ def rating(self, **kwargs):
+ """
+ Rate a TV episode.
+
+ A valid session or guest session ID is required. You can read more
+ about how this works at
+ https://developers.themoviedb.org/3/authentication/how-do-i-generate-a-session-id.
+
+ Args:
+ session_id: (optional) See Authentication.
+ guest_session_id: (optional) See Authentication.
+ value: (required) This is the value of the rating you want to
+ submit. The value is expected to be between 0.5 and 10.0.
+
+ Returns:
+ A dict respresentation of the JSON returned from the API.
+ """
+ path = self._get_tv_id_season_number_episode_number_path('rating')
+
+ payload = {
+ 'value': kwargs.pop('value', None),
+ }
+
+ response = self._POST(path, kwargs, payload)
+ self._set_attrs_to_values(response)
+ return response
+
+ def rating_delete(self, **kwargs):
+ """
+ Remove your rating for a TV episode.
+
+ A valid session or guest session ID is required. You can read more
+ about how this works at
+ https://developers.themoviedb.org/3/authentication/how-do-i-generate-a-session-id.
+
+ Args:
+ session_id: (optional) See Authentication.
+ guest_session_id: (optional) See Authentication.
+
+ Returns:
+ A dict respresentation of the JSON returned from the API.
+ """
+ path = self._get_tv_id_season_number_episode_number_path('rating')
+
+ payload = {
+ 'value': kwargs.pop('value', None),
+ }
+
+ response = self._DELETE(path, kwargs, payload)
+ self._set_attrs_to_values(response)
+ return response
+
+ def videos(self, **kwargs):
+ """
+ Get the videos that have been added to a TV episode.
+
+ Args:
+ language: (optional) ISO 639 code.
+
+ Returns:
+ A dict respresentation of the JSON returned from the API.
+ """
+ path = self._get_tv_id_season_number_episode_number_path('videos')
+
+ response = self._GET(path, kwargs)
+ self._set_attrs_to_values(response)
+ return response
+
+
+class TV_Episode_Groups(TMDB):
+ """
+ TV Episode Groups functionality.
+
+ See: https://developers.themoviedb.org/3/tv-episode-groups
+ """
+ BASE_PATH = 'tv/episode_group'
+ URLS = {
+ 'info': '/{id}',
+ }
+
+ def __init__(self, id):
+ super(TV_Episode_Groups, self).__init__()
+ self.id = id
+
+ def info(self, **kwargs):
+ """
+ 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
+
+ Args:
+ language: (optional) ISO 639 code.
+
+ Returns:
+ A dict respresentation of the JSON returned from the API.
+ """
+ path = self._get_id_path('info')
+
+ response = self._GET(path, kwargs)
+ self._set_attrs_to_values(response)
+ return response
+
+
+class TV_Changes(TMDB):
+ """
+ Changes functionality for TV Series, Season and Episode.
+
+ See: https://developers.themoviedb.org/3/tv/get-tv-changes
+ https://developers.themoviedb.org/3/tv-seasons/get-tv-season-changes
+ https://developers.themoviedb.org/3/tv-episodes/get-tv-episode-changes
+ """
+ BASE_PATH = 'tv'
+ URLS = {
+ 'series': '/{id}/changes', # id => tv_id
+ 'season': '/season/{id}/changes', # id => season_id
+ 'episode': '/episode/{id}/changes', # id => episode_id
+ }
+
+ def __init__(self, id=0):
+ super(TV_Changes, self).__init__()
+ self.id = id
+
+ def series(self, **kwargs):
+ """
+ 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 season
+ changes and episode changes methods to look these up individually.
+
+ Args:
+ start_date: (optional) Filter the results with a start date.
+ Expected format is 'YYYY-MM-DD'.
+ end_date: (optional) Filter the results with a end date.
+ Expected format is 'YYYY-MM-DD'.
+ page: (optional) Minimum 1, maximum 1000, default 1.
+
+ Returns:
+ A dict respresentation of the JSON returned from the API.
+ """
+ path = self._get_id_path('series')
+
+ response = self._GET(path, kwargs)
+ self._set_attrs_to_values(response)
+ return response
+
+ def season(self, **kwargs):
+ """
+ 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.
+
+ Args:
+ start_date: (optional) Filter the results with a start date.
+ Expected format is 'YYYY-MM-DD'.
+ end_date: (optional) Filter the results with a end date.
+ Expected format is 'YYYY-MM-DD'.
+ page: (optional) Minimum 1, maximum 1000, default 1.
+
+ Returns:
+ A dict respresentation of the JSON returned from the API.
+ """
+ path = self._get_id_path('season')
+
+ response = self._GET(path, kwargs)
+ self._set_attrs_to_values(response)
+ return response
+
+ def episode(self, **kwargs):
+ """
+ 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.
+
+ Args:
+ start_date: (optional) Filter the results with a start date.
+ Expected format is 'YYYY-MM-DD'.
+ end_date: (optional) Filter the results with a end date.
+ Expected format is 'YYYY-MM-DD'.
+ page: (optional) Minimum 1, maximum 1000, default 1.
+
+ Returns:
+ A dict respresentation of the JSON returned from the API.
+ """
+ path = self._get_id_path('episode')
+
+ response = self._GET(path, kwargs)
+ self._set_attrs_to_values(response)
+ return response
+
+
+class Networks(TMDB):
+ """
+ Networks functionality.
+
+ See: https://developers.themoviedb.org/3/networks
+ """
+ BASE_PATH = 'network'
+ URLS = {
+ 'info': '/{id}',
+ 'alternative_names': '/{id}/alternative_names',
+ 'images': '/{id}/images',
+ }
+
+ def __init__(self, id):
+ super(Networks, self).__init__()
+ self.id = id
+
+ def info(self, **kwargs):
+ """
+ Get the details of a network.
+
+ Args:
+ None
+
+ Returns:
+ A dict respresentation of the JSON returned from the API.
+ """
+ path = self._get_id_path('info')
+
+ response = self._GET(path, kwargs)
+ self._set_attrs_to_values(response)
+ return response
+
+ def alternative_names(self, **kwargs):
+ """
+ Get the alternative names of a network.
+
+ Args:
+ None
+
+ Returns:
+ A dict representation of the JSON returned from the API.
+ """
+ path = self._get_id_path('alternative_names')
+
+ response = self._GET(path, kwargs)
+ self._set_attrs_to_values(response)
+ return response
+
+ def images(self, **kwargs):
+ """
+ Get a 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 https://developers.themoviedb.org/3/getting-started/images.
+
+ Args:
+ None
+
+ Returns:
+ A dict representation of the JSON returned from the API.
+ """
+ path = self._get_id_path('images')
+
+ response = self._GET(path, kwargs)
+ self._set_attrs_to_values(response)
+ return response