updating latest plexposters

This commit is contained in:
2019-11-26 04:27:57 +00:00
parent 1c76e1801c
commit c6d10808ad
19 changed files with 1079 additions and 87 deletions

BIN
.DS_Store vendored

Binary file not shown.

View File

@@ -1,5 +1,7 @@
import sys
import os
version = sys.version
expand = os.path.expanduser('~')
print(version)
var = os.environ.get('VIRTUAL_ENV', expand)
print(var, expand)

View File

@@ -0,0 +1,78 @@
Creating a config class
can have a toml/ini in ~/.config/module/config.toml to read the values in
os.path.expanduser('~') - expands the tilde to the home directory
suggestion: save configs in ~/.config/module
os.environ.get('VIRTUAL_ENVs', expand) - will get the env variable and fall back to the default afterwards if its not specified.
Method to read from an os env, and then fall back on another way if it doesnt exist:
def get(self, key, default=None, cast=None):
""" Returns the specified configuration value or <default> if not found.
Parameters:
key (str): Configuration variable to load in the format '<section>.<variable>'.
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')

View File

@@ -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

View File

@@ -0,0 +1,294 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1" />
<meta name="generator" content="pdoc 0.7.2" />
<title>plex_posters.config API documentation</title>
<meta name="description" content="" />
<link href='https://cdnjs.cloudflare.com/ajax/libs/normalize/8.0.0/normalize.min.css' rel='stylesheet'>
<link href='https://cdnjs.cloudflare.com/ajax/libs/10up-sanitize.css/8.0.0/sanitize.min.css' rel='stylesheet'>
<link href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/styles/github.min.css" rel="stylesheet">
<style>.flex{display:flex !important}body{line-height:1.5em}#content{padding:20px}#sidebar{padding:30px;overflow:hidden}.http-server-breadcrumbs{font-size:130%;margin:0 0 15px 0}#footer{font-size:.75em;padding:5px 30px;border-top:1px solid #ddd;text-align:right}#footer p{margin:0 0 0 1em;display:inline-block}#footer p:last-child{margin-right:30px}h1,h2,h3,h4,h5{font-weight:300}h1{font-size:2.5em;line-height:1.1em}h2{font-size:1.75em;margin:1em 0 .50em 0}h3{font-size:1.4em;margin:25px 0 10px 0}h4{margin:0;font-size:105%}a{color:#058;text-decoration:none;transition:color .3s ease-in-out}a:hover{color:#e82}.title code{font-weight:bold}h2[id^="header-"]{margin-top:2em}.ident{color:#900}pre code{background:#f8f8f8;font-size:.8em;line-height:1.4em}code{background:#f2f2f1;padding:1px 4px;overflow-wrap:break-word}h1 code{background:transparent}pre{background:#f8f8f8;border:0;border-top:1px solid #ccc;border-bottom:1px solid #ccc;margin:1em 0;padding:1ex}#http-server-module-list{display:flex;flex-flow:column}#http-server-module-list div{display:flex}#http-server-module-list dt{min-width:10%}#http-server-module-list p{margin-top:0}.toc ul,#index{list-style-type:none;margin:0;padding:0}#index code{background:transparent}#index h3{border-bottom:1px solid #ddd}#index ul{padding:0}#index h4{font-weight:bold}#index h4 + ul{margin-bottom:.6em}@media (min-width:200ex){#index .two-column{column-count:2}}@media (min-width:300ex){#index .two-column{column-count:3}}dl{margin-bottom:2em}dl dl:last-child{margin-bottom:4em}dd{margin:0 0 1em 3em}#header-classes + dl > dd{margin-bottom:3em}dd dd{margin-left:2em}dd p{margin:10px 0}.name{background:#eee;font-weight:bold;font-size:.85em;padding:5px 10px;display:inline-block;min-width:40%}.name:hover{background:#e0e0e0}.name > span:first-child{white-space:nowrap}.name.class > span:nth-child(2){margin-left:.4em}.inherited{color:#999;border-left:5px solid #eee;padding-left:1em}.inheritance em{font-style:normal;font-weight:bold}.desc h2{font-weight:400;font-size:1.25em}.desc h3{font-size:1em}.desc dt code{background:inherit}.source summary,.git-link-div{color:#666;text-align:right;font-weight:400;font-size:.8em;text-transform:uppercase}.source summary > *{white-space:nowrap;cursor:pointer}.git-link{color:inherit;margin-left:1em}.source pre{max-height:500px;overflow:auto;margin:0}.source pre code{font-size:12px;overflow:visible}.hlist{list-style:none}.hlist li{display:inline}.hlist li:after{content:',\2002'}.hlist li:last-child:after{content:none}.hlist .hlist{display:inline;padding-left:1em}img{max-width:100%}.admonition{padding:.1em .5em;margin-bottom:1em}.admonition-title{font-weight:bold}.admonition.note,.admonition.info,.admonition.important{background:#aef}.admonition.todo,.admonition.versionadded,.admonition.tip,.admonition.hint{background:#dfd}.admonition.warning,.admonition.versionchanged,.admonition.deprecated{background:#fd4}.admonition.error,.admonition.danger,.admonition.caution{background:lightpink}</style>
<style media="screen and (min-width: 700px)">@media screen and (min-width:700px){#sidebar{width:30%}#content{width:70%;max-width:100ch;padding:3em 4em;border-left:1px solid #ddd}pre code{font-size:1em}.item .name{font-size:1em}main{display:flex;flex-direction:row-reverse;justify-content:flex-end}.toc ul ul,#index ul{padding-left:1.5em}.toc > ul > li{margin-top:.5em}}</style>
<style media="print">@media print{#sidebar h1{page-break-before:always}.source{display:none}}@media print{*{background:transparent !important;color:#000 !important;box-shadow:none !important;text-shadow:none !important}a[href]:after{content:" (" attr(href) ")";font-size:90%}a[href][title]:after{content:none}abbr[title]:after{content:" (" attr(title) ")"}.ir a:after,a[href^="javascript:"]:after,a[href^="#"]:after{content:""}pre,blockquote{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}img{max-width:100% !important}@page{margin:0.5cm}p,h2,h3{orphans:3;widows:3}h1,h2,h3,h4,h5,h6{page-break-after:avoid}}</style>
</head>
<body>
<main>
<article id="content">
<header>
<h1 class="title">Module <code>plex_posters.config</code></h1>
</header>
<section id="section-intro">
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">from plex_posters.lib import export
import toml
import os
from typing import Union
__all__ = []
@export
class plexPosterConfig:
&#34;&#34;&#34;Handles the config options for the module
Attributes
----------
config_file : dict
Contains the config options. See `plexPosterConfig.read_config()`
for the data structure.
&#34;&#34;&#34;
def __init__(self, path: str) -&gt; None:
self.config_file = self.read_config(path)
@staticmethod
def read_config(path: str) -&gt; Union[dict, None]:
&#34;&#34;&#34;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: {&#39;plex_posters&#39; : {&#39;foo&#39;: &#39;bar&#39;}}
&#34;&#34;&#34;
path += &#39;config.toml&#39; if path[-1] == &#39;/&#39; else &#39;/config.toml&#39;
try:
with open(path, &#39;r+&#39;) 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(&#39;~/.config/plex-posters/&#39;)
)
print(inst.config_file)</code></pre>
</details>
</section>
<section>
</section>
<section>
</section>
<section>
</section>
<section>
<h2 class="section-title" id="header-classes">Classes</h2>
<dl>
<dt id="plex_posters.config.plexPosterConfig"><code class="flex name class">
<span>class <span class="ident">plexPosterConfig</span></span>
<span>(</span><span>path)</span>
</code></dt>
<dd>
<section class="desc"><p>Handles the config options for the module</p>
<h2 id="attributes">Attributes</h2>
<dl>
<dt><strong><code>config_file</code></strong> :&ensp;<code>dict</code></dt>
<dd>Contains the config options. See <a title="plex_posters.config.plexPosterConfig.read_config" href="#plex_posters.config.plexPosterConfig.read_config"><code>plexPosterConfig.read_config()</code></a>
for the data structure.</dd>
</dl></section>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">class plexPosterConfig:
&#34;&#34;&#34;Handles the config options for the module
Attributes
----------
config_file : dict
Contains the config options. See `plexPosterConfig.read_config()`
for the data structure.
&#34;&#34;&#34;
def __init__(self, path: str) -&gt; None:
self.config_file = self.read_config(path)
@staticmethod
def read_config(path: str) -&gt; Union[dict, None]:
&#34;&#34;&#34;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: {&#39;plex_posters&#39; : {&#39;foo&#39;: &#39;bar&#39;}}
&#34;&#34;&#34;
path += &#39;config.toml&#39; if path[-1] == &#39;/&#39; else &#39;/config.toml&#39;
try:
with open(path, &#39;r+&#39;) 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</code></pre>
</details>
<h3>Static methods</h3>
<dl>
<dt id="plex_posters.config.plexPosterConfig.read_config"><code class="name flex">
<span>def <span class="ident">read_config</span></span>(<span>path)</span>
</code></dt>
<dd>
<section class="desc"><p>Reads the toml config file from <code>path</code> if it exists.</p>
<h2 id="parameters">Parameters</h2>
<dl>
<dt><strong><code>path</code></strong> :&ensp;<code>str</code></dt>
<dd>Path to config file.</dd>
</dl>
<h2 id="returns">Returns</h2>
<dl>
<dt><code>Union</code>[<code>dict</code>, <code>None</code>]</dt>
<dd>
<p>Returns a dict if the file is found else returns nothing.</p>
<p>The dict contains a key for each header. Each key contains a
dictionary containing a key, value pair for each config under
that header.</p>
<p>Example:</p>
<p>[plex_posters]
foo = bar</p>
<p>Returns a dict: {'plex_posters' : {'foo': 'bar'}}</p>
</dd>
</dl></section>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">@staticmethod
def read_config(path: str) -&gt; Union[dict, None]:
&#34;&#34;&#34;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: {&#39;plex_posters&#39; : {&#39;foo&#39;: &#39;bar&#39;}}
&#34;&#34;&#34;
path += &#39;config.toml&#39; if path[-1] == &#39;/&#39; else &#39;/config.toml&#39;
try:
with open(path, &#39;r+&#39;) as config_file:
config_file = toml.load(config_file)
return config_file
except FileNotFoundError:
pass</code></pre>
</details>
</dd>
</dl>
<h3>Methods</h3>
<dl>
<dt id="plex_posters.config.plexPosterConfig.get"><code class="name flex">
<span>def <span class="ident">get</span></span>(<span>self, key, default=None, cast=None)</span>
</code></dt>
<dd>
<section class="desc"></section>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">def get(self, key: str, default: str = None, cast: callable = None):
pass</code></pre>
</details>
</dd>
</dl>
</dd>
</dl>
</section>
</article>
<nav id="sidebar">
<h1>Index</h1>
<div class="toc">
<ul></ul>
</div>
<ul id="index">
<li><h3>Super-module</h3>
<ul>
<li><code><a title="plex_posters" href="../index.html">plex_posters</a></code></li>
</ul>
</li>
<li><h3><a href="#header-classes">Classes</a></h3>
<ul>
<li>
<h4><code><a title="plex_posters.config.plexPosterConfig" href="#plex_posters.config.plexPosterConfig">plexPosterConfig</a></code></h4>
<ul class="">
<li><code><a title="plex_posters.config.plexPosterConfig.get" href="#plex_posters.config.plexPosterConfig.get">get</a></code></li>
<li><code><a title="plex_posters.config.plexPosterConfig.read_config" href="#plex_posters.config.plexPosterConfig.read_config">read_config</a></code></li>
</ul>
</li>
</ul>
</li>
</ul>
</nav>
</main>
<footer id="footer">
<p>Generated by <a href="https://pdoc3.github.io/pdoc"><cite>pdoc</cite> 0.7.2</a>.</p>
</footer>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/highlight.min.js"></script>
<script>hljs.initHighlightingOnLoad()</script>
</body>
</html>

View File

@@ -0,0 +1,379 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1" />
<meta name="generator" content="pdoc 0.7.2" />
<title>plex_posters API documentation</title>
<meta name="description" content="" />
<link href='https://cdnjs.cloudflare.com/ajax/libs/normalize/8.0.0/normalize.min.css' rel='stylesheet'>
<link href='https://cdnjs.cloudflare.com/ajax/libs/10up-sanitize.css/8.0.0/sanitize.min.css' rel='stylesheet'>
<link href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/styles/github.min.css" rel="stylesheet">
<style>.flex{display:flex !important}body{line-height:1.5em}#content{padding:20px}#sidebar{padding:30px;overflow:hidden}.http-server-breadcrumbs{font-size:130%;margin:0 0 15px 0}#footer{font-size:.75em;padding:5px 30px;border-top:1px solid #ddd;text-align:right}#footer p{margin:0 0 0 1em;display:inline-block}#footer p:last-child{margin-right:30px}h1,h2,h3,h4,h5{font-weight:300}h1{font-size:2.5em;line-height:1.1em}h2{font-size:1.75em;margin:1em 0 .50em 0}h3{font-size:1.4em;margin:25px 0 10px 0}h4{margin:0;font-size:105%}a{color:#058;text-decoration:none;transition:color .3s ease-in-out}a:hover{color:#e82}.title code{font-weight:bold}h2[id^="header-"]{margin-top:2em}.ident{color:#900}pre code{background:#f8f8f8;font-size:.8em;line-height:1.4em}code{background:#f2f2f1;padding:1px 4px;overflow-wrap:break-word}h1 code{background:transparent}pre{background:#f8f8f8;border:0;border-top:1px solid #ccc;border-bottom:1px solid #ccc;margin:1em 0;padding:1ex}#http-server-module-list{display:flex;flex-flow:column}#http-server-module-list div{display:flex}#http-server-module-list dt{min-width:10%}#http-server-module-list p{margin-top:0}.toc ul,#index{list-style-type:none;margin:0;padding:0}#index code{background:transparent}#index h3{border-bottom:1px solid #ddd}#index ul{padding:0}#index h4{font-weight:bold}#index h4 + ul{margin-bottom:.6em}@media (min-width:200ex){#index .two-column{column-count:2}}@media (min-width:300ex){#index .two-column{column-count:3}}dl{margin-bottom:2em}dl dl:last-child{margin-bottom:4em}dd{margin:0 0 1em 3em}#header-classes + dl > dd{margin-bottom:3em}dd dd{margin-left:2em}dd p{margin:10px 0}.name{background:#eee;font-weight:bold;font-size:.85em;padding:5px 10px;display:inline-block;min-width:40%}.name:hover{background:#e0e0e0}.name > span:first-child{white-space:nowrap}.name.class > span:nth-child(2){margin-left:.4em}.inherited{color:#999;border-left:5px solid #eee;padding-left:1em}.inheritance em{font-style:normal;font-weight:bold}.desc h2{font-weight:400;font-size:1.25em}.desc h3{font-size:1em}.desc dt code{background:inherit}.source summary,.git-link-div{color:#666;text-align:right;font-weight:400;font-size:.8em;text-transform:uppercase}.source summary > *{white-space:nowrap;cursor:pointer}.git-link{color:inherit;margin-left:1em}.source pre{max-height:500px;overflow:auto;margin:0}.source pre code{font-size:12px;overflow:visible}.hlist{list-style:none}.hlist li{display:inline}.hlist li:after{content:',\2002'}.hlist li:last-child:after{content:none}.hlist .hlist{display:inline;padding-left:1em}img{max-width:100%}.admonition{padding:.1em .5em;margin-bottom:1em}.admonition-title{font-weight:bold}.admonition.note,.admonition.info,.admonition.important{background:#aef}.admonition.todo,.admonition.versionadded,.admonition.tip,.admonition.hint{background:#dfd}.admonition.warning,.admonition.versionchanged,.admonition.deprecated{background:#fd4}.admonition.error,.admonition.danger,.admonition.caution{background:lightpink}</style>
<style media="screen and (min-width: 700px)">@media screen and (min-width:700px){#sidebar{width:30%}#content{width:70%;max-width:100ch;padding:3em 4em;border-left:1px solid #ddd}pre code{font-size:1em}.item .name{font-size:1em}main{display:flex;flex-direction:row-reverse;justify-content:flex-end}.toc ul ul,#index ul{padding-left:1.5em}.toc > ul > li{margin-top:.5em}}</style>
<style media="print">@media print{#sidebar h1{page-break-before:always}.source{display:none}}@media print{*{background:transparent !important;color:#000 !important;box-shadow:none !important;text-shadow:none !important}a[href]:after{content:" (" attr(href) ")";font-size:90%}a[href][title]:after{content:none}abbr[title]:after{content:" (" attr(title) ")"}.ir a:after,a[href^="javascript:"]:after,a[href^="#"]:after{content:""}pre,blockquote{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}img{max-width:100% !important}@page{margin:0.5cm}p,h2,h3{orphans:3;widows:3}h1,h2,h3,h4,h5,h6{page-break-after:avoid}}</style>
</head>
<body>
<main>
<article id="content">
<header>
<h1 class="title">Module <code>plex_posters</code></h1>
</header>
<section id="section-intro">
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">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(
&#39;T_movie_poster_porn_scraper&#39;, bound=&#34;movie_poster_porn_scraper&#34;
)
@export
class movie_poster_porn_scraper(object):
&#34;&#34;&#34;Poster scraper
Attributes
----------
reddit_instance : praw.Reddit
A praw instance connected to Reddit
&#34;&#34;&#34;
def __init__(self, instance: praw.Reddit) -&gt; None:
&#34;&#34;&#34;
Parameters
----------
instance : praw.Reddit
A praw instance connected to Reddit
&#34;&#34;&#34;
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,
) -&gt; T_movie_poster_porn_scraper:
&#34;&#34;&#34;`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
&#34;&#34;&#34;
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,
) -&gt; T_movie_poster_porn_scraper:
&#34;&#34;&#34;
&#34;&#34;&#34;
self._poster_urls: Dict = {}
for post in self.reddit_instance.subreddit(&#39;MoviePosterPorn&#39;).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):
&#34;&#34;&#34;download the posters
Returns
-------
self
&#34;&#34;&#34;
for title, url in self._poster_urls.items():
r = requests.get(url)
with open(&#39;posters/&#39; + title + &#39;.jpg&#39;, &#39;wb&#39;) as p:
p.write(r.content)
return self</code></pre>
</details>
</section>
<section>
<h2 class="section-title" id="header-submodules">Sub-modules</h2>
<dl>
<dt><code class="name"><a title="plex_posters.config" href="config/index.html">plex_posters.config</a></code></dt>
<dd>
<section class="desc"></section>
</dd>
<dt><code class="name"><a title="plex_posters.lib" href="lib/index.html">plex_posters.lib</a></code></dt>
<dd>
<section class="desc"></section>
</dd>
</dl>
</section>
<section>
</section>
<section>
</section>
<section>
<h2 class="section-title" id="header-classes">Classes</h2>
<dl>
<dt id="plex_posters.movie_poster_porn_scraper"><code class="flex name class">
<span>class <span class="ident">movie_poster_porn_scraper</span></span>
<span>(</span><span>instance)</span>
</code></dt>
<dd>
<section class="desc"><p>Poster scraper</p>
<h2 id="attributes">Attributes</h2>
<dl>
<dt><strong><code>reddit_instance</code></strong> :&ensp;<code>praw.Reddit</code></dt>
<dd>A praw instance connected to Reddit</dd>
</dl>
<h2 id="parameters">Parameters</h2>
<dl>
<dt><strong><code>instance</code></strong> :&ensp;<code>praw.Reddit</code></dt>
<dd>A praw instance connected to Reddit</dd>
</dl></section>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">class movie_poster_porn_scraper(object):
&#34;&#34;&#34;Poster scraper
Attributes
----------
reddit_instance : praw.Reddit
A praw instance connected to Reddit
&#34;&#34;&#34;
def __init__(self, instance: praw.Reddit) -&gt; None:
&#34;&#34;&#34;
Parameters
----------
instance : praw.Reddit
A praw instance connected to Reddit
&#34;&#34;&#34;
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,
) -&gt; T_movie_poster_porn_scraper:
&#34;&#34;&#34;`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
&#34;&#34;&#34;
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,
) -&gt; T_movie_poster_porn_scraper:
&#34;&#34;&#34;
&#34;&#34;&#34;
self._poster_urls: Dict = {}
for post in self.reddit_instance.subreddit(&#39;MoviePosterPorn&#39;).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):
&#34;&#34;&#34;download the posters
Returns
-------
self
&#34;&#34;&#34;
for title, url in self._poster_urls.items():
r = requests.get(url)
with open(&#39;posters/&#39; + title + &#39;.jpg&#39;, &#39;wb&#39;) as p:
p.write(r.content)
return self</code></pre>
</details>
<h3>Static methods</h3>
<dl>
<dt id="plex_posters.movie_poster_porn_scraper.create_instance"><code class="name flex">
<span>def <span class="ident">create_instance</span></span>(<span>client_id, client_secret, user_agent)</span>
</code></dt>
<dd>
<section class="desc"><p><code>classmethod</code> to connect to reddit using the api.</p>
<h2 id="parameters">Parameters</h2>
<dl>
<dt><strong><code>client_id</code></strong> :&ensp;<code>str</code></dt>
<dd>a valid client id</dd>
<dt><strong><code>client_secret</code></strong> :&ensp;<code>str</code></dt>
<dd>the secret key for the client</dd>
<dt><strong><code>user_agent</code></strong> :&ensp;<code>str</code></dt>
<dd>a user agent</dd>
</dl></section>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">@classmethod
def create_instance(
cls: Type[T_movie_poster_porn_scraper],
client_id: str,
client_secret: str,
user_agent: str,
) -&gt; T_movie_poster_porn_scraper:
&#34;&#34;&#34;`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
&#34;&#34;&#34;
reddit_instance = praw.Reddit(
client_id=client_id,
client_secret=client_secret,
user_agent=user_agent,
)
return cls(reddit_instance)</code></pre>
</details>
</dd>
</dl>
<h3>Methods</h3>
<dl>
<dt id="plex_posters.movie_poster_porn_scraper.get_hot_posters"><code class="name flex">
<span>def <span class="ident">get_hot_posters</span></span>(<span>self)</span>
</code></dt>
<dd>
<section class="desc"></section>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">def get_hot_posters(
self,
) -&gt; T_movie_poster_porn_scraper:
&#34;&#34;&#34;
&#34;&#34;&#34;
self._poster_urls: Dict = {}
for post in self.reddit_instance.subreddit(&#39;MoviePosterPorn&#39;).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</code></pre>
</details>
</dd>
<dt id="plex_posters.movie_poster_porn_scraper.get_posters"><code class="name flex">
<span>def <span class="ident">get_posters</span></span>(<span>self)</span>
</code></dt>
<dd>
<section class="desc"><p>download the posters</p>
<h2 id="returns">Returns</h2>
<dl>
<dt><code>self</code></dt>
<dd>&nbsp;</dd>
</dl></section>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">def get_posters(self):
&#34;&#34;&#34;download the posters
Returns
-------
self
&#34;&#34;&#34;
for title, url in self._poster_urls.items():
r = requests.get(url)
with open(&#39;posters/&#39; + title + &#39;.jpg&#39;, &#39;wb&#39;) as p:
p.write(r.content)
return self</code></pre>
</details>
</dd>
</dl>
</dd>
</dl>
</section>
</article>
<nav id="sidebar">
<h1>Index</h1>
<div class="toc">
<ul></ul>
</div>
<ul id="index">
<li><h3><a href="#header-submodules">Sub-modules</a></h3>
<ul>
<li><code><a title="plex_posters.config" href="config/index.html">plex_posters.config</a></code></li>
<li><code><a title="plex_posters.lib" href="lib/index.html">plex_posters.lib</a></code></li>
</ul>
</li>
<li><h3><a href="#header-classes">Classes</a></h3>
<ul>
<li>
<h4><code><a title="plex_posters.movie_poster_porn_scraper" href="#plex_posters.movie_poster_porn_scraper">movie_poster_porn_scraper</a></code></h4>
<ul class="">
<li><code><a title="plex_posters.movie_poster_porn_scraper.create_instance" href="#plex_posters.movie_poster_porn_scraper.create_instance">create_instance</a></code></li>
<li><code><a title="plex_posters.movie_poster_porn_scraper.get_hot_posters" href="#plex_posters.movie_poster_porn_scraper.get_hot_posters">get_hot_posters</a></code></li>
<li><code><a title="plex_posters.movie_poster_porn_scraper.get_posters" href="#plex_posters.movie_poster_porn_scraper.get_posters">get_posters</a></code></li>
</ul>
</li>
</ul>
</li>
</ul>
</nav>
</main>
<footer id="footer">
<p>Generated by <a href="https://pdoc3.github.io/pdoc"><cite>pdoc</cite> 0.7.2</a>.</p>
</footer>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/highlight.min.js"></script>
<script>hljs.initHighlightingOnLoad()</script>
</body>
</html>

View File

@@ -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"]

View File

@@ -7,6 +7,7 @@ authors = ["dtomlinson <dtomlinson@panaetius.co.uk>"]
[tool.poetry.dependencies]
python = "^3.8"
praw = "^6.4"
toml = "^0.10.0"
[tool.poetry.dev-dependencies]
pytest = "^3.0"

View File

@@ -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

View File

@@ -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)

View File

@@ -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=}')

View File

@@ -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__)

Binary file not shown.

Binary file not shown.

32
toml/test.toml Normal file
View File

@@ -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"
]

4
toml/toml.py Normal file
View File

@@ -0,0 +1,4 @@
import toml
with open('test.toml', 'r+') as config:
config_file = toml.load(config)

6
toml/toml_test.py Normal file
View File

@@ -0,0 +1,6 @@
import toml
with open('test.toml', 'r+') as config:
config_file = toml.load(config)
print(config_file)