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: + + 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: + + + 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