diff --git a/scripts/opml.py b/scripts/opml.py
new file mode 100644
index 0000000..cc07f39
--- /dev/null
+++ b/scripts/opml.py
@@ -0,0 +1,32 @@
+#!/usr/bin/python3
+
+# class for representing and writing an OPML outline for RSS feeds
+class Opml:
+
+ def __init__(self, title=""):
+ self.title = title
+ self.feeds = []
+
+ @staticmethod
+ def get_date():
+ from datetime import datetime
+ return datetime.now().strftime("%a %b %d %H:%M:%S %Y")
+
+ def add_feed(self, feed, title="", html_url=""):
+ line = ''
+ self.feeds.append(line)
+
+ def get_opml(self):
+ opml = '''
+
+
+ ''' + self.title + '''
+ ''' + self.get_date() + '''
+
+ '''
+ for line in self.feeds:
+ opml += "\n\t\t" + line
+ opml += '''
+
+'''
+ return opml
diff --git a/scripts/options.py b/scripts/options.py
index 63ccb22..ea736ce 100644
--- a/scripts/options.py
+++ b/scripts/options.py
@@ -47,6 +47,8 @@ def options(params):
# general settings
network = True
+ output_format = ""
+ output_filename = None
## verbosity: in addition to the feed, print...
## 0: no messages (suppress errors)
@@ -64,7 +66,7 @@ def options(params):
arg_count = 0
try:
- opts, args = getopt.getopt(params,"A:c:H:hnp:qtUVv", [
+ opts, args = getopt.getopt(params,"A:c:H:hno:p:qtUVv", [
"user-agent=",
"ciphers=",
"compressed",
@@ -72,6 +74,8 @@ def options(params):
"help",
"license",
"non-network",
+ "output=",
+ "output-format=",
"proxy",
"quiet",
"sites",
@@ -86,33 +90,44 @@ def options(params):
error ("Invalid options. See the README or manual for legal rsstube flags.")
sys.exit(2)
for opt, arg in opts:
- if arg == "":
- arg_count += 1
- else:
- arg_count += 2
-
if opt in ("-A", "--user-agent"):
d["user_agent"] = arg
+ arg_count += 2
elif opt == "--ciphers":
d["ciphers"] = arg
+ arg_count += 2
elif opt == "--compressed":
d["compressed"] = True
+ arg_count += 1
elif opt in ("-h", "--help"):
print ("Usage: rsstube [OPTIONS] URL")
# not available yet
# print ("Use `man rsstube` to see the manual for rsstube.")
+ arg_count += 1
sys.exit()
elif opt in ("-H", "--header"):
header.append(arg)
+ arg_count += 2
elif opt in ("--license"):
print(license)
+ arg_count += 1
sys.exit()
elif opt in ("-n", "--non-network"):
network = False
+ arg_count += 1
+ elif opt in ("-o", "--output"):
+ output_filename = arg
+ arg_count += 2
+ elif opt == "--output-format":
+ if str.lower(arg) in ("opml", "url"):
+ output_format = str.lower(arg)
+ arg_count += 2
elif opt in ("-p", "--proxy"):
d["proxy"] = arg
+ arg_count += 2
elif opt in ("-q", "--quiet"):
verbosity = 1
+ arg_count += 1
elif opt in ("--sites"):
print ("Site-specific support:")
for test in sorted(glob.glob(path + "/tests/*.txt")):
@@ -123,30 +138,38 @@ def options(params):
for test in sorted(glob.glob(path + "/tests/generic/*.txt")):
site = test[test.rfind("/")+1:(-4)]
print ("- " + site)
+ arg_count += 1
sys.exit()
elif opt in ("--suppress-errors"):
verbosity = 0
+ arg_count += 1
elif opt == "--tls-max":
d["tls_max"] = arg
+ arg_count += 2
elif opt == "--tls13-ciphers":
d["tls13_ciphers"] = arg
+ arg_count += 2
elif opt in ("-U", "--update"):
update()
+ arg_count += 1
sys.exit()
elif opt in ("-v", "--verbose"):
verbosity = 4
+ arg_count += 1
elif opt == "--verbosity":
v = int(arg)
if v >= 0 and v <= 4:
verbosity = v
else:
print ("Invalid verbosity: " + arg)
+ arg_count += 2
elif opt in ("-V", "--version"):
version = open(path + "/docs/version","r")
# only go to -1 to cut EOL character
print (version.readline()[:-1])
+ arg_count += 1
sys.exit()
d["header"] = header
- return network,verbosity,d,arg_count
+ return network,verbosity,d,arg_count,output_format,output_filename
diff --git a/scripts/rsstube.py b/scripts/rsstube.py
index f7221a2..8f76eb2 100755
--- a/scripts/rsstube.py
+++ b/scripts/rsstube.py
@@ -3,11 +3,16 @@
import sys,importlib
from utils import debug,notify,warn,error,success
+import opml
network = True
verbosity = 3
args = {}
arg_count = 0
+output_format = "url"
+output_opml = None
+output_filename = None
+output = None
config = None
try:
@@ -22,16 +27,29 @@ except FileNotFoundError:
except FileNotFoundError:
# no change
config = None
-file_params = None
+file_params = ""
if not config is None:
for line in config:
line = line.strip()
# comment lines should begin with # after stripping
- if line[0] != "#":
+ if line != "" and line[0] != "#":
file_params += " " + line
from options import options
-def process_args (network,verbosity,args,arg_count,network_new,verbosity_new,args_new,arg_count_new):
+def process_args (
+ network,
+ verbosity,
+ args,
+ arg_count,
+ output_format,
+ output_filename,
+ network_new,
+ verbosity_new,
+ args_new,
+ arg_count_new,
+ output_format_new,
+ output_filename_new
+):
if network_new == False:
network = network_new
if not verbosity_new is None:
@@ -39,18 +57,54 @@ def process_args (network,verbosity,args,arg_count,network_new,verbosity_new,arg
for i in args_new:
args[i] = args_new[i]
arg_count = arg_count_new
- return network,verbosity,args,arg_count
+ if output_format_new != "":
+ output_format = output_format_new
+ if not output_filename_new is None:
+ output_filename = output_filename_new
+ return network,verbosity,args,arg_count,output_format,output_filename
# config file options
-if not file_params == "" and not file_params is None:
- network_new,verbosity_new,args_new,arg_count_new = options(file_params.split())
- network,verbosity,args,arg_count = process_args(network,verbosity,args,arg_count,network_new,verbosity_new,args_new,arg_count_new)
+if not file_params == "":
+ network_new,verbosity_new,args_new,arg_count_new,output_format_new,output_filename_new = options(file_params.split())
+ network,verbosity,args,arg_count,output_format,output_filename = process_args(
+ network,
+ verbosity,
+ args,
+ arg_count,
+ output_format,
+ output_filename,
+ network_new,
+ verbosity_new,
+ args_new,
+ arg_count_new,
+ output_format_new,
+ output_filename_new
+ )
# command-line options
-network_new,verbosity_new,args_new,arg_count_new = options(sys.argv[1:])
-network,verbosity,args,arg_count = process_args(network,verbosity,args,arg_count,network_new,verbosity_new,args_new,arg_count_new)
-#if not verbosity_temp is None:
-# verbosity = verbosity_temp
+network_new,verbosity_new,args_new,arg_count_new,output_format_new,output_filename_new = options(sys.argv[1:])
+network,verbosity,args,arg_count,output_format,output_filename = process_args(
+ network,
+ verbosity,
+ args,
+ arg_count,
+ output_format,
+ output_filename,
+ network_new,
+ verbosity_new,
+ args_new,
+ arg_count_new,
+ output_format_new,
+ output_filename_new
+)
+
+if output_format == "opml":
+ debug ("Formatting output as OPML.", verbosity)
+ output_opml = opml.Opml("rsstube feeds")
+
+if not output_filename is None and output_filename != "":
+ debug ("Output will be saved in " + output_filename, verbosity)
+ output = open(output_filename, "w")
if len(sys.argv) == arg_count+1:
error ("Please provide one or more URL.", verbosity)
@@ -69,7 +123,10 @@ for url in sys.argv[arg_count+1:]:
if feed is None:
error ("Unable to get RSS feed for " + url, verbosity, site)
else:
- success (feed)
+ if not output_opml is None:
+ output_opml.add_feed (feed, site + ": " + url, url)
+ else:
+ success (feed, output)
elif network:
from download_page import download
page = download (None, url, args, verbosity)
@@ -89,7 +146,10 @@ for url in sys.argv[arg_count+1:]:
if feed is None:
notify ("Unable to get RSS feed for " + url + " with " + software + " extractor", verbosity, software)
else:
- success (feed)
+ if not output_opml is None:
+ output_opml.add_feed (feed, software + ": " + url, url)
+ else:
+ success (feed, output)
continue
# try generic extractor even if software is known
@@ -99,6 +159,15 @@ for url in sys.argv[arg_count+1:]:
if feed is None:
error ("Unable to get RSS feed for " + url, verbosity, "generic")
else:
- success (feed)
+ if not output_opml is None:
+ output_opml.add_feed (feed, url, url)
+ else:
+ success (feed, output)
else:
error ("Unable to get RSS feed for " + url + " without downloading page", verbosity)
+
+if not output_opml is None:
+ success (output_opml.get_opml(), output)
+
+if not output is None:
+ output.close()
diff --git a/scripts/utils.py b/scripts/utils.py
index d0e174e..3c36036 100644
--- a/scripts/utils.py
+++ b/scripts/utils.py
@@ -24,10 +24,11 @@ def error (message, verbosity=1, platform=None):
if verbosity >= 1:
print (color.ERR + "[" + str(platform) + "] " + message + color.NC)
-def success (message):
-# caused issues with piping output into other stuff
-# print (color.SUCCESS + message + color.NC)
- print (message)
+def success (message, output):
+ if output is None:
+ print (message)
+ else:
+ output.write (message + "\n")
def search (content, begins_with, ends_with, index=0, reverse=False):
# hack to search based on ends_with being significant
diff --git a/tests/test.sh b/tests/test.sh
index dd56351..26835dc 100755
--- a/tests/test.sh
+++ b/tests/test.sh
@@ -33,7 +33,7 @@ function test_site {
echo "Goal:"
echo "${links[1]}"
- output=$(/usr/bin/python3 ../rsstube "${links[0]}" | tail -1)
+ output=$(/usr/bin/python3 ../rsstube -o "" --output-format url "${links[0]}" | tail -1)
echo "Output:"
echo "${output}"