Merge branch 'opml-export' into 'master'

Add support for exporting to opml format

See merge request World/podcasts!77
This commit is contained in:
Jordan Petridis 2019-01-27 04:48:26 +00:00
commit a2bcd8aa30
8 changed files with 422 additions and 12 deletions

View File

@ -48,12 +48,10 @@ lazy_static! {
.unwrap();
}
#[cfg(test)]
extern crate tempdir;
#[cfg(test)]
lazy_static! {
static ref TEMPDIR: tempdir::TempDir = { tempdir::TempDir::new("podcasts_unit_test").unwrap() };
pub(crate) static ref TEMPDIR: tempdir::TempDir =
{ tempdir::TempDir::new("podcasts_unit_test").unwrap() };
static ref DB_PATH: PathBuf = TEMPDIR.path().join("podcasts.db");
}

View File

@ -23,16 +23,23 @@
use crate::errors::DataError;
use crate::models::Source;
use xml::reader;
use dbqueries;
use xml::{
common::XmlVersion,
reader,
writer::{events::XmlEvent, EmitterConfig},
};
use std::collections::HashSet;
use std::fs;
use std::io::Read;
use std::io::{Read, Write};
use std::path::Path;
// use std::fs::{File, OpenOptions};
use std::fs::File;
// use std::io::BufReader;
use failure::Error;
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
// FIXME: Make it a Diesel model
/// Represents an `outline` xml element as per the `OPML` [specification][spec]
@ -75,6 +82,88 @@ pub fn import_from_file<P: AsRef<Path>>(path: P) -> Result<Vec<Source>, DataErro
import_to_db(content.as_slice()).map_err(From::from)
}
/// Export a file to `P`, taking the feeds from the database and outputing
/// them in opml format.
pub fn export_from_db<P: AsRef<Path>>(path: P, export_title: &str) -> Result<(), Error> {
let file = File::create(path)?;
export_to_file(&file, export_title)
}
/// Export from `Source`s and `Show`s into `F` in OPML format
pub fn export_to_file<F: Write>(file: F, export_title: &str) -> Result<(), Error> {
let mut config = EmitterConfig::new().perform_indent(true);
config.perform_escaping = false;
let mut writer = config.create_writer(file);
let mut events: Vec<XmlEvent<'_>> = Vec::new();
// Set up headers
let doc = XmlEvent::StartDocument {
version: XmlVersion::Version10,
encoding: Some("UTF-8"),
standalone: Some(false),
};
events.push(doc);
let opml: XmlEvent<'_> = XmlEvent::start_element("opml")
.attr("version", "2.0")
.into();
events.push(opml);
let head: XmlEvent<'_> = XmlEvent::start_element("head").into();
events.push(head);
let title_ev: XmlEvent<'_> = XmlEvent::start_element("title").into();
events.push(title_ev);
let title_chars: XmlEvent<'_> = XmlEvent::characters(export_title).into();
events.push(title_chars);
// Close <title> & <head>
events.push(XmlEvent::end_element().into());
events.push(XmlEvent::end_element().into());
let body: XmlEvent<'_> = XmlEvent::start_element("body").into();
events.push(body);
for event in events {
writer.write(event)?;
}
// FIXME: Make this a model of a joined query (http://docs.diesel.rs/diesel/macro.joinable.html)
let shows = dbqueries::get_podcasts()?.into_iter().map(|show| {
let source = dbqueries::get_source_from_id(show.source_id()).unwrap();
(source, show)
});
for (ref source, ref show) in shows {
let title = show.title();
let link = show.link();
let xml_url = source.uri();
let s_ev: XmlEvent<'_> = XmlEvent::start_element("outline")
.attr("text", title)
.attr("title", title)
.attr("type", "rss")
.attr("xmlUrl", xml_url)
.attr("htmlUrl", link)
.into();
let end_ev: XmlEvent<'_> = XmlEvent::end_element().into();
writer.write(s_ev)?;
writer.write(end_ev)?;
}
// Close <body> and <opml>
let end_bod: XmlEvent<'_> = XmlEvent::end_element().into();
writer.write(end_bod)?;
let end_opml: XmlEvent<'_> = XmlEvent::end_element().into();
writer.write(end_opml)?;
Ok(())
}
/// Extracts the `outline` elemnts from a reader `R` and returns a `HashSet` of `Opml` structs.
pub fn extract_sources<R: Read>(reader: R) -> Result<HashSet<Opml>, reader::Error> {
let mut list = HashSet::new();
@ -122,6 +211,43 @@ mod tests {
use super::*;
use chrono::Local;
use failure::Error;
use futures::Future;
use database::{truncate_db, TEMPDIR};
use utils::get_feed;
const URLS: &[(&str, &str)] = {
&[
(
"tests/feeds/2018-01-20-Intercepted.xml",
"https://web.archive.org/web/20180120083840if_/https://feeds.feedburner.\
com/InterceptedWithJeremyScahill",
),
(
"tests/feeds/2018-01-20-LinuxUnplugged.xml",
"https://web.archive.org/web/20180120110314if_/https://feeds.feedburner.\
com/linuxunplugged",
),
(
"tests/feeds/2018-01-20-TheTipOff.xml",
"https://web.archive.org/web/20180120110727if_/https://rss.acast.com/thetipoff",
),
(
"tests/feeds/2018-01-20-StealTheStars.xml",
"https://web.archive.org/web/20180120104957if_/https://rss.art19.\
com/steal-the-stars",
),
(
"tests/feeds/2018-01-20-GreaterThanCode.xml",
"https://web.archive.org/web/20180120104741if_/https://www.greaterthancode.\
com/feed/podcast",
),
(
"tests/feeds/2019-01-27-ACC.xml",
"https://web.archive.org/web/20190127005213if_/https://anticapitalistchronicles.libsyn.com/rss"
),
]
};
#[test]
fn test_extract() -> Result<(), Error> {
@ -184,4 +310,47 @@ mod tests {
assert_eq!(extract_sources(sample1.as_bytes())?, map);
Ok(())
}
#[test]
fn text_export() -> Result<(), Error> {
truncate_db()?;
URLS.iter().for_each(|&(path, url)| {
// Create and insert a Source into db
let s = Source::from_url(url).unwrap();
let feed = get_feed(path, s.id());
feed.index().wait().unwrap();
});
let mut map: HashSet<Opml> = HashSet::new();
let shows = dbqueries::get_podcasts()?.into_iter().map(|show| {
let source = dbqueries::get_source_from_id(show.source_id()).unwrap();
(source, show)
});
for (ref source, ref show) in shows {
let title = show.title().to_string();
// description is an optional field that we don't export
let description = String::new();
let url = source.uri().to_string();
map.insert(Opml {
title,
description,
url,
});
}
let opml_path = TEMPDIR.path().join("podcasts.opml");
export_from_db(opml_path.as_path(), "GNOME Podcasts Subscriptions")?;
let opml_file = File::open(opml_path.as_path())?;
assert_eq!(extract_sources(&opml_file)?, map);
// extract_sources drains the reader its passed
let mut opml_file = File::open(opml_path.as_path())?;
let mut opml_str = String::new();
opml_file.read_to_string(&mut opml_str)?;
assert_eq!(opml_str, include_str!("../tests/export_test.opml"));
Ok(())
}
}

View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<opml version="2.0">
<head>
<title>GNOME Podcasts Subscriptions</title>
</head>
<body>
<outline text="David Harvey's Anti-Capitalist Chronicles" title="David Harvey's Anti-Capitalist Chronicles" type="rss" xmlUrl="https://web.archive.org/web/20190127005213if_/https://anticapitalistchronicles.libsyn.com/rss" htmlUrl="https://www.democracyatwork.info/acc" />
<outline text="Greater Than Code" title="Greater Than Code" type="rss" xmlUrl="https://web.archive.org/web/20180120104741if_/https://www.greaterthancode.com/feed/podcast" htmlUrl="https://www.greaterthancode.com/" />
<outline text="Intercepted with Jeremy Scahill" title="Intercepted with Jeremy Scahill" type="rss" xmlUrl="https://web.archive.org/web/20180120083840if_/https://feeds.feedburner.com/InterceptedWithJeremyScahill" htmlUrl="https://theintercept.com/podcasts" />
<outline text="LINUX Unplugged Podcast" title="LINUX Unplugged Podcast" type="rss" xmlUrl="https://web.archive.org/web/20180120110314if_/https://feeds.feedburner.com/linuxunplugged" htmlUrl="http://www.jupiterbroadcasting.com/" />
<outline text="Steal the Stars" title="Steal the Stars" type="rss" xmlUrl="https://web.archive.org/web/20180120104957if_/https://rss.art19.com/steal-the-stars" htmlUrl="http://tor-labs.com/" />
<outline text="The Tip Off" title="The Tip Off" type="rss" xmlUrl="https://web.archive.org/web/20180120110727if_/https://rss.acast.com/thetipoff" htmlUrl="http://www.acast.com/thetipoff" />
</body>
</opml>

View File

@ -0,0 +1,176 @@
<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:cc="http://web.resource.org/cc/" xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" xmlns:media="http://search.yahoo.com/mrss/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
<channel>
<atom:link href="https://anticapitalistchronicles.libsyn.com/rss" rel="self" type="application/rss+xml"/>
<title>David Harvey's Anti-Capitalist Chronicles</title>
<pubDate>Thu, 17 Jan 2019 05:00:00 +0000</pubDate>
<lastBuildDate>Thu, 17 Jan 2019 05:06:40 +0000</lastBuildDate>
<generator>Libsyn WebEngine 2.0</generator>
<link>https://www.democracyatwork.info/acc</link>
<language>en</language>
<copyright><![CDATA[Democracy at Work 2018]]></copyright>
<docs>https://www.democracyatwork.info/acc</docs>
<managingEditor>info@democracyatwork.info (info@democracyatwork.info)</managingEditor>
<itunes:summary><![CDATA[The Anti-Capitalist Chronicles look at capitalism through a Marxist lens. Support the show on Patreon and get early access to episodes and more: https://www.patreon.com/davidharveyacc]]></itunes:summary>
<image>
<url>https://ssl-static.libsyn.com/p/assets/0/9/2/b/092b811513b710af/ACC_Logo_Final_LibsynSize.png</url>
<title>David Harvey's Anti-Capitalist Chronicles</title>
<link><![CDATA[https://www.democracyatwork.info/acc]]></link>
</image>
<itunes:author>David Harvey</itunes:author>
<itunes:keywords>capitalism,economics,marxism,politics</itunes:keywords>
<itunes:category text="News &amp; Politics"/>
<itunes:category text="Education"></itunes:category>
<itunes:image href="https://ssl-static.libsyn.com/p/assets/0/9/2/b/092b811513b710af/ACC_Logo_Final_LibsynSize.png" />
<itunes:explicit>clean</itunes:explicit>
<itunes:owner>
<itunes:name><![CDATA[Democracy at Work]]></itunes:name>
<itunes:email>acc@democracyatwork.info</itunes:email>
</itunes:owner>
<description><![CDATA[The Anti-Capitalist Chronicles look at capitalism through a Marxist lens. Support the show on Patreon and get early access to episodes and more: https://www.patreon.com/davidharveyacc ]]></description>
<itunes:subtitle><![CDATA[]]></itunes:subtitle>
<itunes:type>episodic</itunes:type>
<item>
<title>The significance of China in the Global Economy</title>
<itunes:title>The significance of China in the Global Economy</itunes:title>
<pubDate>Thu, 17 Jan 2019 05:00:00 +0000</pubDate>
<guid isPermaLink="false"><![CDATA[b64981bbe26247e19e66f1c9c14a332a]]></guid>
<link><![CDATA[http://anticapitalistchronicles.libsyn.com/the-significance-of-china-in-the-global-economy]]></link>
<itunes:image href="https://ssl-static.libsyn.com/p/assets/0/9/2/b/092b811513b710af/ACC_Logo_Final_LibsynSize.png" />
<description><![CDATA[<p>The Chinese economy is now the 2nd largest in the world. Prof. Harvey argues that China's expansion saved capitalism after the 2008 crash.</p>]]></description>
<content:encoded><![CDATA[<p>The Chinese economy is now the 2nd largest in the world. Prof. Harvey argues that China's expansion saved capitalism after the 2008 crash.</p>]]></content:encoded>
<enclosure length="106240847" type="audio/mpeg" url="https://traffic.libsyn.com/secure/anticapitalistchronicles/ACC_S1_E07.mp3?dest-id=796288" />
<itunes:duration>44:15</itunes:duration>
<itunes:explicit>clean</itunes:explicit>
<itunes:keywords />
<itunes:subtitle><![CDATA[The Chinese economy is now the 2nd largest in the world. Prof. Harvey argues that China's expansion saved capitalism after the 2008 crash.]]></itunes:subtitle>
<itunes:summary>The Chinese economy is now the 2nd largest in the world. Prof. Harvey argues that China's expansion saved capitalism after the 2008 crash.</itunes:summary>
<itunes:season>1</itunes:season>
<itunes:episode>7</itunes:episode>
<itunes:episodeType>full</itunes:episodeType>
<itunes:author>Democracy at Work</itunes:author>
</item>
<item>
<title>Does Socialism Affect Freedom?</title>
<itunes:title>Does Socialism Affect Freedom? </itunes:title>
<pubDate>Thu, 03 Jan 2019 05:00:00 +0000</pubDate>
<guid isPermaLink="false"><![CDATA[3fe5b10d8f6045d7a65bc3de64f23277]]></guid>
<link><![CDATA[http://anticapitalistchronicles.libsyn.com/does-socialism-affect-freedom]]></link>
<itunes:image href="https://ssl-static.libsyn.com/p/assets/0/9/2/b/092b811513b710af/ACC_Logo_Final_LibsynSize.png" />
<description><![CDATA[<p>Does socialism require the surrender of individual freedom? The realm of freedom begins when the realm of necessity is left behind. Is freedom of the market real freedom?  And what about justice?  Prof. Harvey tries to answer these questions and more.</p>]]></description>
<content:encoded><![CDATA[<p>Does socialism require the surrender of individual freedom? The realm of freedom begins when the realm of necessity is left behind. Is freedom of the market real freedom?  And what about justice?  Prof. Harvey tries to answer these questions and more.</p>]]></content:encoded>
<enclosure length="60112942" type="audio/mpeg" url="https://traffic.libsyn.com/secure/anticapitalistchronicles/acc_ep06_Does_socialism_affect_freedom.mp3?dest-id=796288" />
<itunes:duration>25:02</itunes:duration>
<itunes:explicit>clean</itunes:explicit>
<itunes:keywords />
<itunes:subtitle><![CDATA[Does socialism require the surrender of individual freedom? The realm of freedom begins when the realm of necessity is left behind. Is freedom of the market real freedom?  And what about justice?  Prof. Harvey tries to answer these questions...]]></itunes:subtitle>
<itunes:summary>Does socialism require the surrender of individual freedom? The realm of freedom begins when the realm of necessity is left behind. Is freedom of the market real freedom?  And what about justice?  Prof. Harvey tries to answer these questions and more.</itunes:summary>
<itunes:season>1</itunes:season>
<itunes:episode>6</itunes:episode>
<itunes:episodeType>full</itunes:episodeType>
<itunes:author>Democracy at Work</itunes:author>
</item>
<item>
<title>The Value of Everything</title>
<itunes:title>"The Value of Everything</itunes:title>
<pubDate>Thu, 13 Dec 2018 05:00:00 +0000</pubDate>
<guid isPermaLink="false"><![CDATA[6ca8e36116924b4f8b1ebeb1b6c35bae]]></guid>
<link><![CDATA[http://anticapitalistchronicles.libsyn.com/the-value-of-everything]]></link>
<itunes:image href="https://ssl-static.libsyn.com/p/assets/0/9/2/b/092b811513b710af/ACC_Logo_Final_LibsynSize.png" />
<description><![CDATA[<p>Prof. Harvey talks about Mariana Mazzucato's new book "The Value of Everything: Making and Taking in the Global Economy."</p>]]></description>
<content:encoded><![CDATA[<p>Prof. Harvey talks about Mariana Mazzucato's new book "The Value of Everything: Making and Taking in the Global Economy."</p>]]></content:encoded>
<enclosure length="60810663" type="audio/mpeg" url="https://traffic.libsyn.com/secure/anticapitalistchronicles/acc_ep05_2018.10_pt2.mp3?dest-id=796288" />
<itunes:duration>25:19</itunes:duration>
<itunes:explicit>clean</itunes:explicit>
<itunes:keywords />
<itunes:subtitle><![CDATA[Prof. Harvey talks about Mariana Mazzucato's new book "The Value of Everything: Making and Taking in the Global Economy."]]></itunes:subtitle>
<itunes:summary>Prof. Harvey talks about Mariana Mazzucato's new book "The Value of Everything: Making and Taking in the Global Economy."</itunes:summary>
<itunes:season>1</itunes:season>
<itunes:episode>5</itunes:episode>
<itunes:episodeType>full</itunes:episodeType>
<itunes:author>Democracy at Work</itunes:author>
</item>
<item>
<title>The Brazilian Elections</title>
<itunes:title>The Brazilian Elections</itunes:title>
<pubDate>Thu, 29 Nov 2018 05:00:00 +0000</pubDate>
<guid isPermaLink="false"><![CDATA[f76a6e81de1c44db847b5424f7b338de]]></guid>
<link><![CDATA[http://anticapitalistchronicles.libsyn.com/the-brazilian-elections]]></link>
<itunes:image href="https://ssl-static.libsyn.com/p/assets/0/9/2/b/092b811513b710af/ACC_Logo_Final_LibsynSize.png" />
<description><![CDATA[<p>Prof. Harvey talks about the recent Brazilian elections and the growing alliance between Neo-liberalism and Right-Wing Populism.</p>]]></description>
<content:encoded><![CDATA[<p>Prof. Harvey talks about the recent Brazilian elections and the growing alliance between Neo-liberalism and Right-Wing Populism.</p>]]></content:encoded>
<enclosure length="72984484" type="audio/mpeg" url="https://traffic.libsyn.com/secure/anticapitalistchronicles/acc_ep04_2018.10_pt1.mp3?dest-id=796288" />
<itunes:duration>30:23</itunes:duration>
<itunes:explicit>clean</itunes:explicit>
<itunes:keywords />
<itunes:subtitle><![CDATA[Prof. Harvey talks about the recent Brazilian elections and the growing alliance between Neo-liberalism and Right-Wing Populism.]]></itunes:subtitle>
<itunes:summary>Prof. Harvey talks about the recent Brazilian elections and the growing alliance between Neo-liberalism and Right-Wing Populism.</itunes:summary>
<itunes:season>1</itunes:season>
<itunes:episode>4</itunes:episode>
<itunes:episodeType>full</itunes:episodeType>
<itunes:author>Democracy at Work</itunes:author>
</item>
<item>
<title>The Financialization of Power</title>
<itunes:title>The Financialization of Power</itunes:title>
<pubDate>Thu, 15 Nov 2018 13:10:00 +0000</pubDate>
<guid isPermaLink="false"><![CDATA[e4641accc886461dbcbef94775532adc]]></guid>
<link><![CDATA[http://anticapitalistchronicles.libsyn.com/the-financialization-of-power]]></link>
<itunes:image href="https://ssl-static.libsyn.com/p/assets/a/4/d/7/a4d743af97f5ad8e/ACC_Logo_IconOnly_LibsynSize.png" />
<description><![CDATA[<p>Financial services become part of GDP in the 1970s and legitimize the power of financial institutions. </p>]]></description>
<content:encoded><![CDATA[<p>Financial services become part of GDP in the 1970s and legitimize the power of financial institutions. </p>]]></content:encoded>
<enclosure length="46686237" type="audio/mpeg" url="https://traffic.libsyn.com/secure/anticapitalistchronicles/acc_ep03_2018.09.mp3?dest-id=796288" />
<itunes:duration>19:27</itunes:duration>
<itunes:explicit>no</itunes:explicit>
<itunes:keywords />
<itunes:subtitle><![CDATA[Financial services become part of GDP in the 1970s and legitimize the power of financial institutions. ]]></itunes:subtitle>
<itunes:summary>Financial services become part of GDP in the 1970s and legitimize the power of financial institutions. </itunes:summary>
<itunes:season>1</itunes:season>
<itunes:episode>3</itunes:episode>
<itunes:episodeType>full</itunes:episodeType>
<itunes:author>Democracy at Work</itunes:author>
</item>
<item>
<title>Contradictions of Neo-Liberalism</title>
<itunes:title>The Contradictions of Neo-Liberalism</itunes:title>
<pubDate>Thu, 15 Nov 2018 13:05:00 +0000</pubDate>
<guid isPermaLink="false"><![CDATA[a4f0d0206b8d4572b8e54764a39c8a48]]></guid>
<link><![CDATA[http://anticapitalistchronicles.libsyn.com/contradictions-of-neo-liberalism]]></link>
<itunes:image href="https://ssl-static.libsyn.com/p/assets/9/b/e/d/9bed97160a2d6344/ACC_Logo_IconOnly_LibsynSize.png" />
<description><![CDATA[<p>The crash of 2008 challenges Neo-Liberalism.</p>]]></description>
<content:encoded><![CDATA[<p>The crash of 2008 challenges Neo-Liberalism.</p>]]></content:encoded>
<enclosure length="45500801" type="audio/mpeg" url="https://traffic.libsyn.com/secure/anticapitalistchronicles/acc_ep02_2018.09.mp3?dest-id=796288" />
<itunes:duration>18:58</itunes:duration>
<itunes:explicit>clean</itunes:explicit>
<itunes:keywords />
<itunes:subtitle><![CDATA[The crash of 2008 challenges Neo-Liberalism.]]></itunes:subtitle>
<itunes:summary>The crash of 2008 challenge Neo-Liberalism.</itunes:summary>
<itunes:season>1</itunes:season>
<itunes:episode>2</itunes:episode>
<itunes:episodeType>full</itunes:episodeType>
<itunes:author>Democracy at Work</itunes:author>
</item>
<item>
<title>A brief history of Neo-Liberalism</title>
<itunes:title>A brief history of Neo-Liberalism</itunes:title>
<pubDate>Mon, 12 Nov 2018 22:01:39 +0000</pubDate>
<guid isPermaLink="false"><![CDATA[3daeb566cd964764a5847da4f2a6a309]]></guid>
<link><![CDATA[http://anticapitalistchronicles.libsyn.com/a-brief-history-of-neo-liberalism]]></link>
<itunes:image href="https://ssl-static.libsyn.com/p/assets/0/7/e/0/07e073ebe1b26e3f/ACC_Logo_IconOnly_LibsynSize.png" />
<description><![CDATA[<p>Prof. David Harvey's pilot episode. He provides a quick history of the rise and growth of Neo-Liberalism. </p> <p>Support the show on Patreon and get early access to episodes and more: https://www.patreon.com/davidharveyacc</p>]]></description>
<content:encoded><![CDATA[<p>Prof. David Harvey's pilot episode. He provides a quick history of the rise and growth of Neo-Liberalism. </p> <p>Support the show on Patreon and get early access to episodes and more: https://www.patreon.com/davidharveyacc</p>]]></content:encoded>
<enclosure length="47001933" type="audio/mpeg" url="https://traffic.libsyn.com/secure/anticapitalistchronicles/acc_ep01_2018.09.mp3?dest-id=796288" />
<itunes:duration>19:35</itunes:duration>
<itunes:explicit>clean</itunes:explicit>
<itunes:keywords />
<itunes:subtitle><![CDATA[Prof. David Harvey's pilot episode. He provides a quick history of the rise and growth of Neo-Liberalism.  Support the show on Patreon and get early access to episodes and more: https://www.patreon.com/davidharveyacc]]></itunes:subtitle>
<itunes:summary>Prof. David Harvey's pilot episode. He provides a quick history of the rise and growth of Neo-Liberalism.
Support the show on Patreon and get early access to episodes and more: https://www.patreon.com/davidharveyacc</itunes:summary>
<itunes:season>1</itunes:season>
<itunes:episode>1</itunes:episode>
<itunes:episodeType>full</itunes:episodeType>
<itunes:author>Democracy at Work</itunes:author>
</item>
</channel>
</rss>

View File

@ -42,4 +42,10 @@ Raw file: https://web.archive.org/web/20180120104741if_/https://www.greaterthanc
Web view: https://web.archive.org/web/20180328083913/https://ellinofreneia.sealabs.net/audio/podcast.rss
Raw file: https://web.archive.org/web/20180328083913if_/https://ellinofreneia.sealabs.net/audio/podcast.rss
Raw file: https://web.archive.org/web/20180328083913if_/https://ellinofreneia.sealabs.net/audio/podcast.rss
#### David Harvey's Anti-Capitalist Chronicles
Web view: https://web.archive.org/web/20190127005213/https://anticapitalistchronicles.libsyn.com/rss
Raw file: https://web.archive.org/web/20190127005213if_/https://anticapitalistchronicles.libsyn.com/rss

View File

@ -12,10 +12,10 @@
<attribute name="label" translatable="yes">_Import Shows</attribute>
<attribute name="action">win.import</attribute>
</item>
<!-- <item> -->
<!-- <attribute name="label" translatable="yes">_Export Shows</attribute> -->
<!-- <attribute name="action">win.export</attribute> -->
<!-- </item> -->
<item>
<attribute name="label" translatable="yes">_Export Shows</attribute>
<attribute name="action">win.export</attribute>
</item>
</section>
<section>
<item>
@ -38,3 +38,4 @@
</section>
</menu>
</interface>

View File

@ -266,6 +266,10 @@ impl App {
weak_win.upgrade().map(|win| utils::on_import_clicked(&win, &sender));
}));
action(&self.window, "export", clone!(sender, weak_win => move |_, _| {
weak_win.upgrade().map(|win| utils::on_export_clicked(&win, &sender));
}));
// Create the action that shows a `gtk::AboutDialog`
action(&self.window, "about", clone!(weak_win => move |_, _| {
weak_win.upgrade().map(|win| about_dialog(&win));

View File

@ -414,6 +414,48 @@ pub(crate) fn on_import_clicked(window: &gtk::ApplicationWindow, sender: &Sender
}
}
pub(crate) fn on_export_clicked(window: &gtk::ApplicationWindow, sender: &Sender<Action>) {
use glib::translate::ToGlib;
use gtk::{FileChooserAction, FileChooserNative, FileFilter, ResponseType};
// Create the FileChooser Dialog
let dialog = FileChooserNative::new(
Some(i18n("Export shows to...").as_str()),
Some(window),
FileChooserAction::Save,
Some(i18n("_Export").as_str()),
Some(i18n("_Cancel").as_str()),
);
// Do not show hidden(.thing) files
dialog.set_show_hidden(false);
// Set a filter to show only xml files
let filter = FileFilter::new();
FileFilterExt::set_name(&filter, Some(i18n("OPML file").as_str()));
filter.add_mime_type("application/xml");
filter.add_mime_type("text/xml");
dialog.add_filter(&filter);
let resp = dialog.run();
debug!("Dialog Response {}", resp);
if resp == ResponseType::Accept.to_glib() {
if let Some(filename) = dialog.get_filename() {
debug!("File selected: {:?}", filename);
rayon::spawn(clone!(sender => move || {
if opml::export_from_db(filename, i18n("GNOME Podcasts Subscriptions").as_str()).is_err() {
let text = i18n("Failed to export podcasts");
sender.send(Action::ErrorNotification(text));
}
}))
} else {
let text = i18n("Selected file could not be accessed.");
sender.send(Action::ErrorNotification(text));
}
}
}
#[cfg(test)]
mod tests {
use super::*;