Skip to content

Instantly share code, notes, and snippets.

@zats
Last active December 6, 2021 01:20
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 zats/b1e6c4829962c9b0fbda783cd26b806d to your computer and use it in GitHub Desktop.
Save zats/b1e6c4829962c9b0fbda783cd26b806d to your computer and use it in GitHub Desktop.
Export OPML from Apple Podcasts app on macOS

Export OPML from Apple Podcasts app on macOS (tested on macOS 12 Monterey)

  1. Download following script
  2. Make downloaded file executable $ chmod +x ~/Download/ApplePodcastToOMPL.js
  3. Run $ ~/Downloads/ApplePodcastToOMPL.js to see the OPML output
  4. If it looks okay, $ ~/Downloads/ApplePodcastToOMPL.js > ~/Downloads/ApplePodcasts.opml

If default sqlite path not working for you, you can specify a custom one $ ~/Downloads/ApplePodcastToOMPL.js /my/custom/MTLibrary.sqlite

#!/usr/bin/env node
const { exit } = require('process');
const PREDEFINED_PATH = "~/Library/Group Containers/243LU875E5.groups.com.apple.podcasts/Documents/MTLibrary.sqlite";
const XML_TEMPLATE = `<?xml version="1.0"?>
<!-- example OPML file -->
<opml version="1.0">
<head>
<title>Overcast Podcast Subscriptions</title>
</head>
<body>
%BODY%
</body>
</opml>`;
const XML_PODCAST_TEMPLATE = `<outline type="rss" title="%TITLE%" text="%TEXT%" xmlUrl="%RSS_URL%" htmlUrl="%WEB_URL%"/>`;
const escapeString = (str) => {
return str.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&apos;');
};
const args = process.argv.slice(2);
const sqlitePath = (args.length >= 1 ? args[0] : PREDEFINED_PATH).replace(/ /g, '\\ ');
console.log(`sqlitePath: "${sqlitePath}"`);
let jsonOutputString;
try {
jsonOutputString = require('child_process').execSync(`sqlite3 ${sqlitePath} "SELECT ZTITLE,ZITEMDESCRIPTION,ZFEEDURL,ZWEBPAGEURL FROM ZMTPODCAST WHERE ZSUBSCRIBED = 1" -json`).toString();
} catch (error) {
console.error(`Failed to parse sqlite file: ${sqlitePath}`);
exit(-1);
}
const jsonOutput = JSON.parse(jsonOutputString);
const bodyArray = jsonOutput.map((podcast) => XML_PODCAST_TEMPLATE
.replace('%TITLE%', escapeString(podcast['ZTITLE']))
.replace('%TEXT%', escapeString(podcast['ZITEMDESCRIPTION']))
.replace('%RSS_URL%', podcast['ZFEEDURL'])
.replace('%WEB_URL%', podcast['ZWEBPAGEURL']));
const output = XML_TEMPLATE.replace('%BODY%', bodyArray.join('\n'));
console.log(output);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment