From c6d10808ad2247ebf7090f09365dcf9187bba214 Mon Sep 17 00:00:00 2001 From: dtomlinson Date: Tue, 26 Nov 2019 04:27:57 +0000 Subject: [PATCH] updating latest plexposters --- .DS_Store | Bin 8196 -> 8196 bytes playground/python-debugging.py | 8 +- plex-posters/__dev/create-config.txt | 78 ++++ plex-posters/__dev/toml-layout.txt | 28 ++ .../html/plex_posters/config/index.html | 294 ++++++++++++++ plex-posters/html/plex_posters/index.html | 379 ++++++++++++++++++ plex-posters/poetry.lock | 35 +- plex-posters/pyproject.toml | 1 + .../src/plex_posters/__dev/__init__.py | 93 +++++ plex-posters/src/plex_posters/__init__.py | 111 ++--- .../__pycache__/__init__.cpython-38.pyc | Bin 2547 -> 196 bytes .../src/plex_posters/config/__init__.py | 95 +++++ plex-posters/src/plex_posters/lib/__init__.py | 2 +- .../lib/__pycache__/__init__.cpython-38.pyc | Bin 415 -> 448 bytes plex-posters/tests/testimport/.DS_Store | Bin 6148 -> 6148 bytes toml/__pycache__/toml.cpython-38.pyc | Bin 0 -> 240 bytes toml/test.toml | 32 ++ toml/toml.py | 4 + toml/toml_test.py | 6 + 19 files changed, 1079 insertions(+), 87 deletions(-) create mode 100644 plex-posters/__dev/create-config.txt create mode 100644 plex-posters/__dev/toml-layout.txt create mode 100644 plex-posters/html/plex_posters/config/index.html create mode 100644 plex-posters/html/plex_posters/index.html create mode 100644 plex-posters/src/plex_posters/__dev/__init__.py create mode 100644 plex-posters/src/plex_posters/config/__init__.py create mode 100644 toml/__pycache__/toml.cpython-38.pyc create mode 100644 toml/test.toml create mode 100644 toml/toml.py create mode 100644 toml/toml_test.py diff --git a/.DS_Store b/.DS_Store index d9def066c81420258cca0e00108a93a1d2667db7..b6ce9aa544650e1665230ea002243ac57f37811f 100644 GIT binary patch delta 41 xcmZp1XmOa}&nUhzU^hRb_+}mf9hS{mBKsIOmI*R#W|#QJGFe~r=f*NVCIA)E4PO8N delta 146 zcmZp1XmOa}&nUSuU^hRb if not found. + Parameters: + key (str): Configuration variable to load in the format '
.'. + default: Default value to use if key not found. + cast (func): Cast the value to the specified type before returning. + """ + try: + # First: check environment variable is set + envkey = 'PLEXAPI_%s' % key.upper().replace('.', '_') + value = os.environ.get(envkey) + if value is None: + # Second: check the config file has attr + section, name = key.lower().split('.') + value = self.data.get(section, {}).get(name, default) + return cast(value) if cast else value + except: # noqa: E722 + return default + + +- To open a config .toml: + toml.load(file) where file has a default value of the ~/.config/module/module.toml + If the toml file doesn't exist, nor has a path been passed in, pass and fall back to other methods (os envs, cli) + +- Have a get method, that checks the config file for the result, and then tries the env variable. + + + +-- set the path to the toml in the __init__.py, check for a config file override path and fall back to default if not + +# Load User Defined Config +DEFAULT_CONFIG_PATH = os.path.expanduser('~/.config/plexapi/config.ini') +CONFIG_PATH = os.environ.get('PLEXAPI_CONFIG_PATH', DEFAULT_CONFIG_PATH) +CONFIG = PlexConfig(CONFIG_PATH) + +- each module/submodule should have the header set to the section it corresponds to in the .toml file. + +e.g a reddit module that needs access to the reddit options should have +__section__ = 'reddit' + +this would correspond to +[reddit] +PLEXPOSTERS_REDDIT_ env var + + +-- the __init__ of the module should instantiate this config class: + +# Load User Defined Config +DEFAULT_CONFIG_PATH = os.path.expanduser('~/.config/plexapi/config.ini') +CONFIG_PATH = os.environ.get('PLEXAPI_CONFIG_PATH', DEFAULT_CONFIG_PATH) +CONFIG = PlexConfig(CONFIG_PATH) + +other modules can import this instantiated class + +the __init__ of the module should also set all the default values as capital vars e.g. + +PLEX_USERNAME = plexPosterConfig.get(f'{__section__}.username') + + + + + + + + + diff --git a/plex-posters/__dev/toml-layout.txt b/plex-posters/__dev/toml-layout.txt new file mode 100644 index 0000000..0ce78c3 --- /dev/null +++ b/plex-posters/__dev/toml-layout.txt @@ -0,0 +1,28 @@ +[name] + +[name.module] +anything generic to the module + +[name.header] +specific values for different sections + +[name.logging] +logging configurations + + + + +[plexapi] + +[plexapi.reddit] +client_id = +client_secret = +user_agent = + +[plexapi.logging] +backup_count = 3 +format = "%(asctime)s %(module)12s:%(lineno)-4s %(levelname)-9s %(message)s" +level = "INFO" +path = ~/.config/plexapi/plexapi.log +rotate_bytes = 512000 +secrets = false diff --git a/plex-posters/html/plex_posters/config/index.html b/plex-posters/html/plex_posters/config/index.html new file mode 100644 index 0000000..f2c6ba0 --- /dev/null +++ b/plex-posters/html/plex_posters/config/index.html @@ -0,0 +1,294 @@ + + + + + + +plex_posters.config API documentation + + + + + + + + + +
+
+
+

Module plex_posters.config

+
+
+
+ +Expand source code + +
from plex_posters.lib import export
+import toml
+import os
+from typing import Union
+
+__all__ = []
+
+
+@export
+class plexPosterConfig:
+
+    """Handles the config options for the module
+
+    Attributes
+    ----------
+    config_file : dict
+        Contains the config options. See `plexPosterConfig.read_config()`
+        for the data structure.
+    """
+
+    def __init__(self, path: str) -> None:
+        self.config_file = self.read_config(path)
+
+    @staticmethod
+    def read_config(path: str) -> Union[dict, None]:
+
+        """Reads the toml config file from `path` if it exists.
+
+        Parameters
+        ----------
+        path : str
+            Path to config file.
+
+        Returns
+        -------
+        Union[dict, None]
+            Returns a dict if the file is found else returns nothing.
+
+            The dict contains a key for each header. Each key contains a
+            dictionary containing a key, value pair for each config under
+            that header.
+
+            Example:
+
+            [plex_posters]
+            foo = bar
+
+            Returns a dict: {'plex_posters' : {'foo': 'bar'}}
+        """
+
+        path += 'config.toml' if path[-1] == '/' else '/config.toml'
+
+        try:
+            with open(path, 'r+') as config_file:
+                config_file = toml.load(config_file)
+            return config_file
+        except FileNotFoundError:
+            pass
+
+    def get(self, key: str, default: str = None, cast: callable = None):
+        pass
+
+
+inst = plexPosterConfig(
+    os.path.expanduser('~/.config/plex-posters/')
+)
+
+print(inst.config_file)
+
+
+
+
+
+
+
+
+
+

Classes

+
+
+class plexPosterConfig +(path) +
+
+

Handles the config options for the module

+

Attributes

+
+
config_file : dict
+
Contains the config options. See plexPosterConfig.read_config() +for the data structure.
+
+
+ +Expand source code + +
class plexPosterConfig:
+
+    """Handles the config options for the module
+
+    Attributes
+    ----------
+    config_file : dict
+        Contains the config options. See `plexPosterConfig.read_config()`
+        for the data structure.
+    """
+
+    def __init__(self, path: str) -> None:
+        self.config_file = self.read_config(path)
+
+    @staticmethod
+    def read_config(path: str) -> Union[dict, None]:
+
+        """Reads the toml config file from `path` if it exists.
+
+        Parameters
+        ----------
+        path : str
+            Path to config file.
+
+        Returns
+        -------
+        Union[dict, None]
+            Returns a dict if the file is found else returns nothing.
+
+            The dict contains a key for each header. Each key contains a
+            dictionary containing a key, value pair for each config under
+            that header.
+
+            Example:
+
+            [plex_posters]
+            foo = bar
+
+            Returns a dict: {'plex_posters' : {'foo': 'bar'}}
+        """
+
+        path += 'config.toml' if path[-1] == '/' else '/config.toml'
+
+        try:
+            with open(path, 'r+') as config_file:
+                config_file = toml.load(config_file)
+            return config_file
+        except FileNotFoundError:
+            pass
+
+    def get(self, key: str, default: str = None, cast: callable = None):
+        pass
+
+

Static methods

+
+
+def read_config(path) +
+
+

Reads the toml config file from path if it exists.

+

Parameters

+
+
path : str
+
Path to config file.
+
+

Returns

+
+
Union[dict, None]
+
+

Returns a dict if the file is found else returns nothing.

+

The dict contains a key for each header. Each key contains a +dictionary containing a key, value pair for each config under +that header.

+

Example:

+

[plex_posters] +foo = bar

+

Returns a dict: {'plex_posters' : {'foo': 'bar'}}

+
+
+
+ +Expand source code + +
@staticmethod
+def read_config(path: str) -> Union[dict, None]:
+
+    """Reads the toml config file from `path` if it exists.
+
+    Parameters
+    ----------
+    path : str
+        Path to config file.
+
+    Returns
+    -------
+    Union[dict, None]
+        Returns a dict if the file is found else returns nothing.
+
+        The dict contains a key for each header. Each key contains a
+        dictionary containing a key, value pair for each config under
+        that header.
+
+        Example:
+
+        [plex_posters]
+        foo = bar
+
+        Returns a dict: {'plex_posters' : {'foo': 'bar'}}
+    """
+
+    path += 'config.toml' if path[-1] == '/' else '/config.toml'
+
+    try:
+        with open(path, 'r+') as config_file:
+            config_file = toml.load(config_file)
+        return config_file
+    except FileNotFoundError:
+        pass
+
+
+
+

Methods

+
+
+def get(self, key, default=None, cast=None) +
+
+
+
+ +Expand source code + +
def get(self, key: str, default: str = None, cast: callable = None):
+    pass
+
+
+
+
+
+
+
+ +
+ + + + + \ No newline at end of file diff --git a/plex-posters/html/plex_posters/index.html b/plex-posters/html/plex_posters/index.html new file mode 100644 index 0000000..7cddfce --- /dev/null +++ b/plex-posters/html/plex_posters/index.html @@ -0,0 +1,379 @@ + + + + + + +plex_posters API documentation + + + + + + + + + +
+
+
+

Module plex_posters

+
+
+
+ +Expand source code + +
from __future__ import annotations
+from .__version__ import __version__  # noqa
+from .lib import export
+from typing import Type, TypeVar, List, Dict
+import praw  # type: ignore
+import requests
+
+__all__ = []  # type: List
+
+
+T_movie_poster_porn_scraper = TypeVar(
+    'T_movie_poster_porn_scraper', bound="movie_poster_porn_scraper"
+)
+
+
+@export
+class movie_poster_porn_scraper(object):
+
+    """Poster scraper
+
+    Attributes
+    ----------
+    reddit_instance : praw.Reddit
+        A praw instance connected to Reddit
+    """
+
+    def __init__(self, instance: praw.Reddit) -> None:
+        """
+        Parameters
+        ----------
+        instance : praw.Reddit
+            A praw instance connected to Reddit
+        """
+        super().__init__()
+        self.reddit_instance = instance
+
+    @classmethod
+    def create_instance(
+        cls: Type[T_movie_poster_porn_scraper],
+        client_id: str,
+        client_secret: str,
+        user_agent: str,
+    ) -> T_movie_poster_porn_scraper:
+        """`classmethod` to connect to reddit using the api.
+
+        Parameters
+        ----------
+        client_id : str
+            a valid client id
+        client_secret : str
+            the secret key for the client
+        user_agent : str
+            a user agent
+        """
+        reddit_instance = praw.Reddit(
+            client_id=client_id,
+            client_secret=client_secret,
+            user_agent=user_agent,
+        )
+
+        return cls(reddit_instance)
+
+    def get_hot_posters(
+        self,
+    ) -> T_movie_poster_porn_scraper:
+        """
+        """
+        self._poster_urls: Dict = {}
+        for post in self.reddit_instance.subreddit('MoviePosterPorn').hot(
+            limit=10
+        ):
+            print(post.title)
+            print(post.url)
+            # print(dir(post))
+            # self._poster_urls.append(post.url)
+            self._poster_urls[post.title] = post.url
+        print(self._poster_urls)
+        return self
+
+    def get_posters(self):
+        """download the posters
+
+        Returns
+        -------
+        self
+        """
+        for title, url in self._poster_urls.items():
+            r = requests.get(url)
+            with open('posters/' + title + '.jpg', 'wb') as p:
+                p.write(r.content)
+        return self
+
+
+
+

Sub-modules

+
+
plex_posters.config
+
+
+
+
plex_posters.lib
+
+
+
+
+
+
+
+
+
+
+

Classes

+
+
+class movie_poster_porn_scraper +(instance) +
+
+

Poster scraper

+

Attributes

+
+
reddit_instance : praw.Reddit
+
A praw instance connected to Reddit
+
+

Parameters

+
+
instance : praw.Reddit
+
A praw instance connected to Reddit
+
+
+ +Expand source code + +
class movie_poster_porn_scraper(object):
+
+    """Poster scraper
+
+    Attributes
+    ----------
+    reddit_instance : praw.Reddit
+        A praw instance connected to Reddit
+    """
+
+    def __init__(self, instance: praw.Reddit) -> None:
+        """
+        Parameters
+        ----------
+        instance : praw.Reddit
+            A praw instance connected to Reddit
+        """
+        super().__init__()
+        self.reddit_instance = instance
+
+    @classmethod
+    def create_instance(
+        cls: Type[T_movie_poster_porn_scraper],
+        client_id: str,
+        client_secret: str,
+        user_agent: str,
+    ) -> T_movie_poster_porn_scraper:
+        """`classmethod` to connect to reddit using the api.
+
+        Parameters
+        ----------
+        client_id : str
+            a valid client id
+        client_secret : str
+            the secret key for the client
+        user_agent : str
+            a user agent
+        """
+        reddit_instance = praw.Reddit(
+            client_id=client_id,
+            client_secret=client_secret,
+            user_agent=user_agent,
+        )
+
+        return cls(reddit_instance)
+
+    def get_hot_posters(
+        self,
+    ) -> T_movie_poster_porn_scraper:
+        """
+        """
+        self._poster_urls: Dict = {}
+        for post in self.reddit_instance.subreddit('MoviePosterPorn').hot(
+            limit=10
+        ):
+            print(post.title)
+            print(post.url)
+            # print(dir(post))
+            # self._poster_urls.append(post.url)
+            self._poster_urls[post.title] = post.url
+        print(self._poster_urls)
+        return self
+
+    def get_posters(self):
+        """download the posters
+
+        Returns
+        -------
+        self
+        """
+        for title, url in self._poster_urls.items():
+            r = requests.get(url)
+            with open('posters/' + title + '.jpg', 'wb') as p:
+                p.write(r.content)
+        return self
+
+

Static methods

+
+
+def create_instance(client_id, client_secret, user_agent) +
+
+

classmethod to connect to reddit using the api.

+

Parameters

+
+
client_id : str
+
a valid client id
+
client_secret : str
+
the secret key for the client
+
user_agent : str
+
a user agent
+
+
+ +Expand source code + +
@classmethod
+def create_instance(
+    cls: Type[T_movie_poster_porn_scraper],
+    client_id: str,
+    client_secret: str,
+    user_agent: str,
+) -> T_movie_poster_porn_scraper:
+    """`classmethod` to connect to reddit using the api.
+
+    Parameters
+    ----------
+    client_id : str
+        a valid client id
+    client_secret : str
+        the secret key for the client
+    user_agent : str
+        a user agent
+    """
+    reddit_instance = praw.Reddit(
+        client_id=client_id,
+        client_secret=client_secret,
+        user_agent=user_agent,
+    )
+
+    return cls(reddit_instance)
+
+
+
+

Methods

+
+
+def get_hot_posters(self) +
+
+
+
+ +Expand source code + +
def get_hot_posters(
+    self,
+) -> T_movie_poster_porn_scraper:
+    """
+    """
+    self._poster_urls: Dict = {}
+    for post in self.reddit_instance.subreddit('MoviePosterPorn').hot(
+        limit=10
+    ):
+        print(post.title)
+        print(post.url)
+        # print(dir(post))
+        # self._poster_urls.append(post.url)
+        self._poster_urls[post.title] = post.url
+    print(self._poster_urls)
+    return self
+
+
+
+def get_posters(self) +
+
+

download the posters

+

Returns

+
+
self
+
 
+
+
+ +Expand source code + +
def get_posters(self):
+    """download the posters
+
+    Returns
+    -------
+    self
+    """
+    for title, url in self._poster_urls.items():
+        r = requests.get(url)
+        with open('posters/' + title + '.jpg', 'wb') as p:
+            p.write(r.content)
+    return self
+
+
+
+
+
+
+
+ +
+ + + + + \ No newline at end of file diff --git a/plex-posters/poetry.lock b/plex-posters/poetry.lock index e281f75..0c699d1 100644 --- a/plex-posters/poetry.lock +++ b/plex-posters/poetry.lock @@ -14,6 +14,12 @@ optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" version = "19.3.0" +[package.extras] +azure-pipelines = ["coverage", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface", "pytest-azurepipelines"] +dev = ["coverage", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface", "sphinx", "pre-commit"] +docs = ["sphinx", "zope.interface"] +tests = ["coverage", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface"] + [[package]] category = "main" description = "Python package for providing Mozilla's CA Bundle." @@ -68,6 +74,9 @@ mypy-extensions = ">=0.4.0,<0.5.0" typed-ast = ">=1.4.0,<1.5.0" typing-extensions = ">=3.7.4" +[package.extras] +dmypy = ["psutil (>=4.0)"] + [[package]] category = "dev" description = "Experimental type system extensions for programs checked with the mypy typechecker." @@ -84,6 +93,9 @@ optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" version = "0.13.1" +[package.extras] +dev = ["pre-commit", "tox"] + [[package]] category = "main" description = "PRAW, an acronym for `Python Reddit API Wrapper`, is a python package that allows for simple access to reddit's API." @@ -97,6 +109,9 @@ prawcore = ">=1.0.1,<2.0" update-checker = ">=0.16" websocket-client = ">=0.54.0" +[package.extras] +dev = ["pre-commit"] + [[package]] category = "main" description = "Low-level communication layer for PRAW 4+." @@ -168,6 +183,10 @@ chardet = ">=3.0.2,<3.1.0" idna = ">=2.5,<2.9" urllib3 = ">=1.21.1,<1.25.0 || >1.25.0,<1.25.1 || >1.25.1,<1.26" +[package.extras] +security = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)"] +socks = ["PySocks (>=1.5.6,<1.5.7 || >1.5.7)", "win-inet-pton"] + [[package]] category = "main" description = "Python 2 and 3 compatibility utilities" @@ -176,6 +195,14 @@ optional = false python-versions = ">=2.6, !=3.0.*, !=3.1.*" version = "1.13.0" +[[package]] +category = "main" +description = "Python Library for Tom's Obvious, Minimal Language" +name = "toml" +optional = false +python-versions = "*" +version = "0.10.0" + [[package]] category = "dev" description = "a fork of Python 2 and 3 ast modules with type comment support" @@ -211,6 +238,11 @@ optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, <4" version = "1.25.7" +[package.extras] +brotli = ["brotlipy (>=0.6.0)"] +secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "ipaddress"] +socks = ["PySocks (>=1.5.6,<1.5.7 || >1.5.7,<2.0)"] + [[package]] category = "dev" description = "A full-featured console (xterm et al.) user interface library" @@ -231,7 +263,7 @@ version = "0.56.0" six = "*" [metadata] -content-hash = "3fb939a7e78632a796efbfa0c358369ccd78a148d5649cd2894380f6e82d5409" +content-hash = "535a624cacbbf26a39bef9870a9f37106b05293cfd93a0b05be181cab1f57628" python-versions = "^3.8" [metadata.hashes] @@ -253,6 +285,7 @@ pygments = ["71e430bc85c88a430f000ac1d9b331d2407f681d6f6aec95e8bcfbc3df5b0127", pytest = ["3f193df1cfe1d1609d4c583838bea3d532b18d6160fd3f55c9447fdca30848ec", "e246cf173c01169b9617fc07264b7b1316e78d7a650055235d6d897bc80d9660"] requests = ["11e007a8a2aa0323f5a921e9e6a2d7e4e67d9877e85773fba9ba6419025cbeb4", "9cf5292fcd0f598c671cfc1e0d7d1a7f13bb8085e9a590f48c010551dc6c4b31"] six = ["1f1b7d42e254082a9db6279deae68afb421ceba6158efa6131de7b3003ee93fd", "30f610279e8b2578cab6db20741130331735c781b56053c59c4076da27f06b66"] +toml = ["229f81c57791a41d65e399fc06bf0848bab550a9dfd5ed66df18ce5f05e73d5c", "235682dd292d5899d361a811df37e04a8828a5b1da3115886b73cf81ebc9100e", "f1db651f9657708513243e61e6cc67d101a39bad662eaa9b5546f789338e07a3"] typed-ast = ["1170afa46a3799e18b4c977777ce137bb53c7485379d9706af8a59f2ea1aa161", "18511a0b3e7922276346bcb47e2ef9f38fb90fd31cb9223eed42c85d1312344e", "262c247a82d005e43b5b7f69aff746370538e176131c32dda9cb0f324d27141e", "2b907eb046d049bcd9892e3076c7a6456c93a25bebfe554e931620c90e6a25b0", "354c16e5babd09f5cb0ee000d54cfa38401d8b8891eefa878ac772f827181a3c", "48e5b1e71f25cfdef98b013263a88d7145879fbb2d5185f2a0c79fa7ebbeae47", "4e0b70c6fc4d010f8107726af5fd37921b666f5b31d9331f0bd24ad9a088e631", "630968c5cdee51a11c05a30453f8cd65e0cc1d2ad0d9192819df9978984529f4", "66480f95b8167c9c5c5c87f32cf437d585937970f3fc24386f313a4c97b44e34", "71211d26ffd12d63a83e079ff258ac9d56a1376a25bc80b1cdcdf601b855b90b", "7954560051331d003b4e2b3eb822d9dd2e376fa4f6d98fee32f452f52dd6ebb2", "838997f4310012cf2e1ad3803bce2f3402e9ffb71ded61b5ee22617b3a7f6b6e", "95bd11af7eafc16e829af2d3df510cecfd4387f6453355188342c3e79a2ec87a", "bc6c7d3fa1325a0c6613512a093bc2a2a15aeec350451cbdf9e1d4bffe3e3233", "cc34a6f5b426748a507dd5d1de4c1978f2eb5626d51326e43280941206c209e1", "d755f03c1e4a51e9b24d899561fec4ccaf51f210d52abdf8c07ee2849b212a36", "d7c45933b1bdfaf9f36c579671fec15d25b06c8398f113dab64c18ed1adda01d", "d896919306dd0aa22d0132f62a1b78d11aaf4c9fc5b3410d3c666b818191630a", "fdc1c9bbf79510b76408840e009ed65958feba92a88833cdceecff93ae8fff66", "ffde2fbfad571af120fcbfbbc61c72469e72f550d676c3342492a9dfdefb8f12"] typing-extensions = ["091ecc894d5e908ac75209f10d5b4f118fbdb2eb1ede6a63544054bb1edb41f2", "910f4656f54de5993ad9304959ce9bb903f90aadc7c67a0bef07e678014e892d", "cf8b63fedea4d89bab840ecbb93e75578af28f76f66c35889bd7065f5af88575"] update-checker = ["59cfad7f9a0ee99f95f1dfc60f55bf184937bcab46a7270341c2c33695572453", "70e39446fccf77b21192cf7a8214051fa93a636dc3b5c8b602b589d100a168b8"] diff --git a/plex-posters/pyproject.toml b/plex-posters/pyproject.toml index b03194f..b7daa39 100644 --- a/plex-posters/pyproject.toml +++ b/plex-posters/pyproject.toml @@ -7,6 +7,7 @@ authors = ["dtomlinson "] [tool.poetry.dependencies] python = "^3.8" praw = "^6.4" +toml = "^0.10.0" [tool.poetry.dev-dependencies] pytest = "^3.0" diff --git a/plex-posters/src/plex_posters/__dev/__init__.py b/plex-posters/src/plex_posters/__dev/__init__.py new file mode 100644 index 0000000..6d47aac --- /dev/null +++ b/plex-posters/src/plex_posters/__dev/__init__.py @@ -0,0 +1,93 @@ +from __future__ import annotations +from .__version__ import __version__ # noqa +from .lib import export +from typing import Type, TypeVar, List, Dict +import praw # type: ignore +import requests + +__all__ = [] # type: List + +__header__ = 'plex_posters' +# __section__ = 'module' + +T_movie_poster_porn_scraper = TypeVar( + 'T_movie_poster_porn_scraper', bound="movie_poster_porn_scraper" +) + + +@export +class movie_poster_porn_scraper(object): + + """Poster scraper + + Attributes + ---------- + reddit_instance : praw.Reddit + A praw instance connected to Reddit + """ + + def __init__(self, instance: praw.Reddit) -> None: + """ + Parameters + ---------- + instance : praw.Reddit + A praw instance connected to Reddit + """ + super().__init__() + self.reddit_instance = instance + + @classmethod + def create_instance( + cls: Type[T_movie_poster_porn_scraper], + client_id: str, + client_secret: str, + user_agent: str, + ) -> T_movie_poster_porn_scraper: + """`classmethod` to connect to reddit using the api. + + Parameters + ---------- + client_id : str + a valid client id + client_secret : str + the secret key for the client + user_agent : str + a user agent + """ + reddit_instance = praw.Reddit( + client_id=client_id, + client_secret=client_secret, + user_agent=user_agent, + ) + + return cls(reddit_instance) + + def get_hot_posters( + self, + ) -> T_movie_poster_porn_scraper: + """ + """ + self._poster_urls: Dict = {} + for post in self.reddit_instance.subreddit('MoviePosterPorn').hot( + limit=10 + ): + print(post.title) + print(post.url) + # print(dir(post)) + # self._poster_urls.append(post.url) + self._poster_urls[post.title] = post.url + print(self._poster_urls) + return self + + def get_posters(self): + """download the posters + + Returns + ------- + self + """ + for title, url in self._poster_urls.items(): + r = requests.get(url) + with open('posters/' + title + '.jpg', 'wb') as p: + p.write(r.content) + return self diff --git a/plex-posters/src/plex_posters/__init__.py b/plex-posters/src/plex_posters/__init__.py index df3ac86..41c801e 100644 --- a/plex-posters/src/plex_posters/__init__.py +++ b/plex-posters/src/plex_posters/__init__.py @@ -1,88 +1,35 @@ -from __future__ import annotations -from .__version__ import __version__ # noqa -from .lib import export -from typing import Type, TypeVar, List, Dict -import praw # type: ignore -import requests +from plex_posters.config import plexPosterConfig +import logging -__all__ = [] # type: List +__header__ = 'plex_posters' -T_movie_poster_porn_scraper = TypeVar( - 'T_movie_poster_porn_scraper', bound="movie_poster_porn_scraper" +# Load User Defined Config +DEFAULT_CONFIG_PATH = os.path.expanduser('~/.config/plexapi/config.ini') +CONFIG_PATH = os.environ.get('PLEXAPI_CONFIG_PATH', DEFAULT_CONFIG_PATH) +_config = plexPosterConfig(CONFIG_PATH) + + +# Logging Configuration +log = logging.getLogger(__header__) +logfile = CONFIG.get('log.path') +logformat = CONFIG.get( + 'log.format', + '%(asctime)s %(module)12s:%(lineno)-4s %(levelname)-9s %(message)s', ) +loglevel = CONFIG.get('log.level', 'INFO').upper() +loghandler = logging.NullHandler() +if logfile: # pragma: no cover + logbackups = CONFIG.get('log.backup_count', 3, int) + logbytes = CONFIG.get('log.rotate_bytes', 512000, int) + loghandler = RotatingFileHandler( + os.path.expanduser(logfile), 'a', logbytes, logbackups + ) -@export -class movie_poster_porn_scraper(object): - - """Poster scraper - Attributes - ---------- - reddit_instance : praw.Reddit - A praw instance connected to Reddit - """ - - def __init__(self, instance: praw.Reddit) -> None: - """ - Parameters - ---------- - instance : praw.Reddit - A praw instance connected to Reddit - """ - super().__init__() - self.reddit_instance = instance - - @classmethod - def create_instance( - cls: Type[T_movie_poster_porn_scraper], - client_id: str, - client_secret: str, - user_agent: str, - ) -> T_movie_poster_porn_scraper: - """Connect to reddit - Parameters - ---------- - client_id : str - a valid client id - client_secret : str - the secret key for the client - user_agent : str - a user agent - """ - reddit_instance = praw.Reddit( - client_id=client_id, - client_secret=client_secret, - user_agent=user_agent, - ) - - return cls(reddit_instance) - - def get_hot_posters( - self: T_movie_poster_porn_scraper, - ) -> T_movie_poster_porn_scraper: - """ - """ - self._poster_urls: Dict = {} - for post in self.reddit_instance.subreddit('MoviePosterPorn').hot( - limit=10 - ): - print(post.title) - print(post.url) - # print(dir(post)) - # self._poster_urls.append(post.url) - self._poster_urls[post.title] = post.url - print(self._poster_urls) - return self - - def get_posters(self: T_movie_poster_porn_scraper): - """download the posters - Returns - ------- - self - """ - for title, url in self._poster_urls.items(): - r = requests.get(url) - with open('posters/' + title + '.jpg', 'wb') as p: - p.write(r.content) - return self +loghandler.setFormatter(logging.Formatter(logformat)) +log.addHandler(loghandler) +log.setLevel(loglevel) +logfilter = SecretsFilter() +if CONFIG.get('log.show_secrets', '').lower() != 'true': + log.addFilter(logfilter) diff --git a/plex-posters/src/plex_posters/__pycache__/__init__.cpython-38.pyc b/plex-posters/src/plex_posters/__pycache__/__init__.cpython-38.pyc index 67026d357462c4a9ad31f5ffe9f34aab8e166ed7..4bd21f8adaa0a36dea913710ab3d72549ceb2de2 100644 GIT binary patch delta 125 zcmew?e1tI}l$V!_0SGoMy%Q?~q#uJg$bb>ZaRB0C4j_@j5XF$f7|fu_6vY$Z;~Ehk z;2#{~8WilO$#{z^K0YHgF(tJqK7J)b5fe}~h?pG494LH?!zMRBr8Fni4rJPAAZ7pn D(l{Bl literal 2547 zcmb7G&2QX96rb_edcC{NM+>Aa2)95bOC&jQK?Q+Y2?>=blopU7GLC1Gdhyzunc1dU zZQzhdJyqOM4#~0q5t=KfUibr$P>xR%&Fe}f#`FNi9e($~C_t1K&Nhj=yv16~XYD6e+~dw?gayn$B+Ta*4(tOO&oS>S5_O)TJrUKN zv@9!?s=O-YGdi71l80PKm`W1Z;_*krN~p-u&b_@M*DdV7PK9>v#WNi8)8s zW3ok=%e=#G>~PmO?W5x?IC5Esbq^hI*Mo7J^^Podfz2H{aSKNCN0cqFMHu;yi9_Nx z0CG`u;F$5x>&E(UYIqPr`&22BZ;lj~X1LrmGZdV$TqSuaRa#~|{4gAfba&;hnKnDj zJIrj@?8>UL8FdK+MGnX@ zJt34#=!7Hy8aQZ;e`>5*PenT55T)89G_Y}Mx!E;4^c<0PCu9bvswffGDUPpU`hWL|Y~Rqf zR6-yHM6R~8Lh5!_EkIQLr9y$+I|gGi)`H#Ph&Ov)~FWWk{p{{Hg3addueoY9Xgu?Wg_Jdsmp z4XYI6YvQ>MLQbGQ;px+3sL~VKH{|w&-X95Kpc?u0K1#tTl;k1Qr59Ou}3?shgRQ0^wl&L}OW zE3G79RhA@%OLfO^HQX!@Dkk1SGvC4PeYCYVW%7&{1?+!Of){ZM)}gt9k&{jB`L@%# z6j*d=X?`J^kHLhk5nO#>g1|BoSV!6}@=bx65PNEGn3vna!!`VL5I%PB^+m*+*xi6m zgSn|W9x>5gwTV}TIro%7#xz3={>|F$k1_EK0e2yZyMUrkeao?d8Hn}Vb1nKe!eNoy diff --git a/plex-posters/src/plex_posters/config/__init__.py b/plex-posters/src/plex_posters/config/__init__.py new file mode 100644 index 0000000..530dad7 --- /dev/null +++ b/plex-posters/src/plex_posters/config/__init__.py @@ -0,0 +1,95 @@ +from plex_posters.lib import export +from plex_posters import __header__ as header +from typing import Union +import os +import toml + +__all__ = [] +__section__ = 'CONFIG' + + +@export +class plexPosterConfig: + + """Handles the config options for the module + + Attributes + ---------- + config_file : dict + Contains the config options. See `plexPosterConfig.read_config()` + for the data structure. + """ + + def __init__(self, path: str) -> None: + self.config_file = self.read_config(path) + self.module_name = header.lower() + + @staticmethod + def read_config(path: str) -> Union[dict, None]: + + """Reads the toml config file from `path` if it exists. + + Parameters + ---------- + path : str + Path to config file. + + Returns + ------- + Union[dict, None] + Returns a dict if the file is found else returns nothing. + + The dict contains a key for each header. Each key contains a + dictionary containing a key, value pair for each config under + that header. + + Example: + + [plexposters] + + [plexposters.foo] + foo = bar + + Returns a dict: {'plexposters' : {foo: {'foo': 'bar'}}} + """ + + path += 'config.toml' if path[-1] == '/' else '/config.toml' + + try: + with open(path, 'r+') as config_file: + config_file = toml.load(config_file) + return config_file + except FileNotFoundError: + pass + + def get(self, key: str, default: str = None, cast: callable = None): + env_key = f"{header}_{key.upper().replace('.', '_')}" + print(self.config_file) + try: + section, name = key.lower().split('.') + value = self.config_file[self.module_name][section][name] + print(f'{env_key} found in config.toml') + return cast(value) if cast else value + except KeyError: + print(f'{env_key} not found in config.toml') + value = os.environ.get(env_key) + if value is not None: + print(f'{env_key} found in an environment variable') + else: + print(f'{env_key} not found in an environment variable.') + value = default + print(f'{env_key} set to default {default}') + return cast(value) if cast else value + + +inst = plexPosterConfig( + os.path.expanduser('~/.config/plex-posters/') +) + +# print(inst.config_file, '\n') + +# print(f"{os.environ.get('VIRTUAL_ENV')=}") + +PLEX_USERNAME = inst.get(f'plex.password', 'smile') + +print(f'{PLEX_USERNAME=}') diff --git a/plex-posters/src/plex_posters/lib/__init__.py b/plex-posters/src/plex_posters/lib/__init__.py index 5071333..83aa23c 100644 --- a/plex-posters/src/plex_posters/lib/__init__.py +++ b/plex-posters/src/plex_posters/lib/__init__.py @@ -1,7 +1,7 @@ import sys -def export(fn): +def export(fn: callable) -> callable: mod = sys.modules[fn.__module__] if hasattr(mod, '__all__'): mod.__all__.append(fn.__name__) diff --git a/plex-posters/src/plex_posters/lib/__pycache__/__init__.cpython-38.pyc b/plex-posters/src/plex_posters/lib/__pycache__/__init__.cpython-38.pyc index d290bc8bedd3884783f6b702aad58c3266b7828c..03d14b0d9c65f105434d86e2631e74d157c54b72 100644 GIT binary patch delta 195 zcmbQwe1JJ2l$V!_0SFk&@5J5#(vLwLWWWsMH~?|643J1+NMX!jh+;@(Ol3@An!}XB zoWjz=62+9l7|fu_`Vy#u!B3Ov7E@Z@Ew-Z6lG38QiC!&?EECU*^A#}xC2uk3=BKP= zDB_v;x!9QxD4@w)#0nDUNKVYjNleN~Edq%a@t_jCU?ny{y`?#+c8ow?F~~FyW&oa( BCxrk2 delta 182 zcmX@WJfGPsl$V!_0SNk!U5z~kq#uJg$bbpRaRB0C5g?JmkiwY55XF$fl)~J?5XG3n z7|fu_@)9V+;5RX$MHQs@7F%LLL26#gEsps3yu{qp_;^jGTTE$rx0rMDQ&ut*aZl!A qEVkqYs?=mEVg^!0Aa)TqD!~Kf-r}$UYAMZ0wPOVGia~~RFarRy!6R4z diff --git a/plex-posters/tests/testimport/.DS_Store b/plex-posters/tests/testimport/.DS_Store index b31b79c3275a9d38c59135f7bac25e7e163e5e27..208eee210675b0784d8c6b02377b875b036dc430 100644 GIT binary patch delta 33 ncmZoMXffE(#w?<1prfE`P^+U*ZD?izWE&b7nQxxOoFxJPnX?G9 delta 33 ncmZoMXffE(#w=oDYO154Yf!7BP;F>r1Y}#7TW+4koFxJPn+6EY diff --git a/toml/__pycache__/toml.cpython-38.pyc b/toml/__pycache__/toml.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1bae0e50aaeb40ab2ad7147a059a4a3fad864ed1 GIT binary patch literal 240 zcmWIL<>g`kg5uyiv5`RfF^GcR|Az;w(umF3~H=&&{b~D$>?uy~P4z-(txx zNX@&&mYkoLmYIHwB_}^I| literal 0 HcmV?d00001 diff --git a/toml/test.toml b/toml/test.toml new file mode 100644 index 0000000..3ed204c --- /dev/null +++ b/toml/test.toml @@ -0,0 +1,32 @@ + +title = "TOML Example" + +[owner] +name = "Tom Preston-Werner" +dob = 1979-05-27T07:32:00-08:00 # First class dates + +[database] +server = "192.168.1.1" +ports = [ 8001, 8001, 8002 ] +connection_max = 5000 +enabled = true + +[servers] + + # Indentation (tabs and/or spaces) is allowed but not required + [servers.alpha] + ip = "10.0.0.1" + dc = "eqdc10" + + [servers.beta] + ip = "10.0.0.2" + dc = "eqdc10" + +[clients] +data = [ ["gamma", "delta"], [1, 2] ] + +# Line breaks are OK when inside arrays +hosts = [ + "alpha", + "omega" +] diff --git a/toml/toml.py b/toml/toml.py new file mode 100644 index 0000000..b730e72 --- /dev/null +++ b/toml/toml.py @@ -0,0 +1,4 @@ +import toml + +with open('test.toml', 'r+') as config: + config_file = toml.load(config) diff --git a/toml/toml_test.py b/toml/toml_test.py new file mode 100644 index 0000000..bd89dfe --- /dev/null +++ b/toml/toml_test.py @@ -0,0 +1,6 @@ +import toml + +with open('test.toml', 'r+') as config: + config_file = toml.load(config) + +print(config_file)