Skip to content

Instantly share code, notes, and snippets.

@nikdoof
Created April 30, 2022 08:08
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nikdoof/8bb9de7f91aad8dcca8fb69c1f70f6ac to your computer and use it in GitHub Desktop.
Save nikdoof/8bb9de7f91aad8dcca8fb69c1f70f6ac to your computer and use it in GitHub Desktop.
MiniFlux2OPML - a OPML exporter for Miniflux
#!/usr/bin/env python3
"""
Miniflux2OPML
A tool designed to extract a user's OPML file from a Miniflux installation,
with added support for outputting the OPML into JSON.
Copyright 2022 Andrew Williams
License: MIT
"""
import argparse
import json
import sys
import xml.etree.ElementTree as et
from collections import defaultdict
from urllib.parse import urljoin
import requests
__version__ = '1.0.0'
def download_opml(url, token):
# Use Miniflux's export API endpoint to grab the OPML
endpoint = urljoin(url, '/v1/export')
headers = {'X-Auth-Token': token}
req = requests.get(endpoint, headers=headers)
if req.ok:
return req.content.decode('utf-8')
def parse_opml(fobj):
xml_doc = et.fromstring(fobj)
return xml2dict(xml_doc)
def xml2dict(elem):
output = {elem.tag: {} if elem.attrib else None}
children = list(elem)
if children:
dd = defaultdict(list)
for dc in map(xml2dict, children):
for key, value in dc.items():
dd[key].append(value)
output = {elem.tag: {key: value[0] if len(value) == 1 and key !=
'outline' else value for key, value in dd.items()}}
if elem.text:
text = elem.text.strip()
if children or elem.attrib:
if text:
output[elem.tag]['#text'] = text
else:
output[elem.tag] = text
if elem.attrib:
for key, value in elem.attrib.items():
output[elem.tag]['_' + key] = value
return output
def main():
parser = argparse.ArgumentParser('miniflux2opml {0}'.format(__version__))
parser.add_argument('--url', '-u', help='URL of the Miniflux instance', required=True)
parser.add_argument('--token', '-t', help='Your Miniflux API token', required=True)
parser.add_argument('--json', '-j', type=argparse.FileType('w'), help='Path to write the converted JSON OPML file')
parser.add_argument('--output', '-o', type=argparse.FileType('w'), help='Path to write the XML OPML file', default=sys.stdout)
args = parser.parse_args()
opml = download_opml(args.url, args.token)
if opml:
args.output.write(str(opml))
if args.json:
doc = parse_opml(opml)
args.json.write(json.dumps(doc))
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment