summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--.gitignore156
-rwxr-xr-xpodweb.py102
-rw-r--r--readme.md2
-rw-r--r--requirements.txt6
4 files changed, 266 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..b92086e
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,156 @@
+# ---> Python
+# Byte-compiled / optimized / DLL files
+__pycache__/
+*.py[cod]
+*$py.class
+
+# C extensions
+*.so
+
+# Distribution / packaging
+.Python
+build/
+develop-eggs/
+dist/
+downloads/
+eggs/
+.eggs/
+lib/
+lib64/
+parts/
+sdist/
+var/
+wheels/
+share/python-wheels/
+*.egg-info/
+.installed.cfg
+*.egg
+MANIFEST
+
+# PyInstaller
+#  Usually these files are written by a python script from a template
+#  before PyInstaller builds the exe, so as to inject date/other infos into it.
+*.manifest
+*.spec
+
+# Installer logs
+pip-log.txt
+pip-delete-this-directory.txt
+
+# Unit test / coverage reports
+htmlcov/
+.tox/
+.nox/
+.coverage
+.coverage.*
+.cache
+nosetests.xml
+coverage.xml
+*.cover
+*.py,cover
+.hypothesis/
+.pytest_cache/
+cover/
+
+# Translations
+*.mo
+*.pot
+
+# Django stuff:
+*.log
+local_settings.py
+db.sqlite3
+db.sqlite3-journal
+
+# Flask stuff:
+instance/
+.webassets-cache
+
+# Scrapy stuff:
+.scrapy
+
+# Sphinx documentation
+docs/_build/
+
+# PyBuilder
+.pybuilder/
+target/
+
+# Jupyter Notebook
+.ipynb_checkpoints
+
+# IPython
+profile_default/
+ipython_config.py
+
+# pyenv
+#   For a library or package, you might want to ignore these files since the code is
+#   intended to run in multiple environments; otherwise, check them in:
+# .python-version
+
+# pipenv
+#   According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
+#   However, in case of collaboration, if having platform-specific dependencies or dependencies
+#   having no cross-platform support, pipenv may install dependencies that don't work, or not
+#   install all needed dependencies.
+#Pipfile.lock
+
+# poetry
+#   Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
+#   This is especially recommended for binary packages to ensure reproducibility, and is more
+#   commonly ignored for libraries.
+#   https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
+#poetry.lock
+
+# PEP 582; used by e.g. github.com/David-OConnor/pyflow
+__pypackages__/
+
+# Celery stuff
+celerybeat-schedule
+celerybeat.pid
+
+# SageMath parsed files
+*.sage.py
+
+# Environments
+.env
+.venv
+env/
+venv/
+ENV/
+env.bak/
+venv.bak/
+
+# Spyder project settings
+.spyderproject
+.spyproject
+
+# Rope project settings
+.ropeproject
+
+# mkdocs documentation
+/site
+
+# mypy
+.mypy_cache/
+.dmypy.json
+dmypy.json
+
+# Pyre type checker
+.pyre/
+
+# pytype static type analyzer
+.pytype/
+
+# Cython debug symbols
+cython_debug/
+
+# PyCharm
+#  JetBrains specific template is maintainted in a separate JetBrains.gitignore that can
+#  be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
+#  and can be added to the global gitignore or merged into this file.  For a more nuclear
+#  option (not recommended) you can uncomment the following to ignore the entire idea folder.
+#.idea/
+
+debug_*
+*.opml
\ No newline at end of file
diff --git a/podweb.py b/podweb.py
new file mode 100755
index 0000000..4ac83ea
--- /dev/null
+++ b/podweb.py
@@ -0,0 +1,102 @@
+#!/usr/bin/env python3
+import os
+import copy
+import click
+import yaml
+import xml
+
+import podcastparser
+import urllib
+
+global options
+options = {"DEBUG": False, "serverlist": os.path.normpath(os.path.join(os.path.expanduser("~/.config/podweb"), "serverlist")), "podcastpath": ""}
+
+class PodWeb():
+    
+    def __init__(self, DEBUG : bool = False) -> None:
+        self.options = options
+        self.options.update({"DEBUG": DEBUG})
+        self.servers = []
+        self.DEFAULT_SERVERLIST_HEADING = '''## You can add podcast xml feeds here.  
+## You can also optionally add categories and names for the podcasts.
+## The order of category, name, and url does not matter.
+## Here are some example entries:
+##  - category: example category
+##    name: example podcast 1
+##    url: https://example.com/feed.xml
+##  - name: example podcast 2
+##    url: example.com/feed2.xml
+'''
+
+        if self.options["DEBUG"]:
+            self.config_path = "."
+            self.config_filepath = "debug_config.yaml"
+            self.options["serverlist"] = "debug_serverlist"
+        else:
+            self.config_path = os.path.normpath(os.path.expanduser("~/.config/podweb"))
+            self.config_filepath = os.path.join(self.config_path, "config.yaml")
+
+        self._load_config()
+        self._load_serverlist()
+
+    def _load_config(self) -> None:
+
+        if not os.path.exists(self.config_path): os.makedirs(self.config_path)
+
+        if not os.path.isfile(self.config_filepath):
+            with open(self.config_filepath, "w+") as f:
+                yaml.dump(self.options, f)
+        
+        else:
+            with open(self.config_filepath, "r+") as f:
+                self.options.update(yaml.full_load(f))
+
+    def _update_config(self, changed_option : dict) -> None:
+        '''Makes a change to the config file'''
+        with open(self.options["serverlist"], "w+") as f:
+            config_options = yaml.full_load(f)
+            config_options.update(changed_option)
+            f.write(config_options)
+
+    def _load_serverlist(self) -> list:
+        '''Loads the contents of the serverlist'''
+        self._create_serverlist()
+        with open(self.options["serverlist"], "r+") as f:
+            content = yaml.full_load(f)
+        if content:
+            self.servers = content
+
+    def _create_serverlist(self) -> None:
+        '''Checks if the serverlist does not exist and creates it if not'''
+        if not os.path.isfile(self.options["serverlist"]):
+            with open(self.options["serverlist"], "w+") as f:
+                f.write(self.DEFAULT_SERVERLIST_HEADING)
+
+    def _update_serverlist(self) -> None:
+        '''This is destructive and overwrites the current serverlist with the stored serverlist'''
+        with open(self.options["serverlist"], "w+") as f:
+            f.write(self.DEFAULT_SERVERLIST_HEADING)
+        if len(self.servers):
+            with open(self.options["serverlist"], "a") as f:
+                yaml.dump(self.servers, f)
+    
+    def add_podcast(self, feedurl : str, name = None, category = None) -> None:
+        for i in self.servers:
+            iparse = urllib.parse.urlparse(i["url"])
+            feedparse = urllib.parse.urlparse(feedurl)
+            if iparse.hostname == feedparse.hostname and iparse.path == feedparse.path:
+                return None
+        parsed = podcastparser.parse(feedurl, urllib.request.urlopen(feedurl))
+        if name:
+            new_feed = {"url": feedurl, "name": name}
+        else:
+            new_feed = {"url": feedurl, "name": parsed["title"]}
+        if category: new_feed.update({"category": category})
+        self.servers.append(new_feed)
+        self._update_serverlist()
+
+    def import_opml(self, opml_path : str):
+        pass
+
+
+testweb = PodWeb(DEBUG = True)
\ No newline at end of file
diff --git a/readme.md b/readme.md
new file mode 100644
index 0000000..e423f9a
--- /dev/null
+++ b/readme.md
@@ -0,0 +1,2 @@
+#PodWeb
+A small in-progress cli podcast fetcher
\ No newline at end of file
diff --git a/requirements.txt b/requirements.txt
new file mode 100644
index 0000000..d9967a0
--- /dev/null
+++ b/requirements.txt
@@ -0,0 +1,6 @@
+podcastparser
+pyyaml
+click
+pytest >= 4.6
+pytest-cov
+coverage