Bug 126120

Summary: amaroK Should be Able to Import and Export Podcast Subscription
Product: [Applications] amarok Reporter: Pratik Patel <pkpatel88>
Component: PodcastAssignee: Amarok Developers <amarok-bugs-dist>
Status: RESOLVED FIXED    
Severity: wishlist CC: andreaswuest, bart.cerneels, bjoernv, dev-kde, kde, kuba.serafinowski, tatum.rashad
Priority: NOR    
Version: 2.3-GIT   
Target Milestone: 2.4.0   
Platform: Ubuntu   
OS: Linux   
Latest Commit: Version Fixed In: 2.4
Attachments: screenshot of location export to OPML action.
No OPML export in Amarok 2.8 found

Description Pratik Patel 2006-04-23 17:55:09 UTC
Version:           1.3.9 (using KDE KDE 3.5.2)
Installed from:    Ubuntu Packages
Compiler:          gcc 4.0.3 
OS:                Linux

amaroK should be able to import Podcast Subscriptions from other podcast receivers (i.e. Juice (formerly known as iPodder) and Apple iTunes). This can be done by reading an OPML file exported by those podcasting receivers.

amorok should also export Podcast Subscriptions threw OPML files.
Comment 1 Martin Aumueller 2006-04-23 22:52:51 UTC
This sounds like a very reasonable wish to me.
Additionally, using a list of podcast subscriptions stored as OPML (just as akregator does for rss feeds) instead of storing the subscriptions only into the database (as is done in 1.4, starting with beta3), would have the advantage of the subscriptions being persistent despite database backends being changed.
This would also make amaroK make a little bit more consistent with itself, as I view my podcast subscriptions as configuration data, and all other configuration data is stored in plain files, not in the database. 
Comment 2 Mark Kretschmann 2006-04-23 23:31:55 UTC
100% agreed, would be cool to have.
Comment 3 Seb Ruiz 2006-04-27 02:16:10 UTC
Bart wrote an OPML importer, lets use it!
Comment 4 Christian Herenz 2006-06-01 23:02:20 UTC
Yeah, but as OPML is getting more stronger, amarok should also have an OPML import /export function built in
Comment 5 Keith Hughitt 2007-06-23 23:57:44 UTC
I also would love to see an option to export (or import for that matter) podcasts from other apps like google reader, Rhythmbox,etc. Along the same lines, it would be nice if there were an easy way to export your songs metadata (ratings,etc), without having to manually copy the database tables.
Comment 6 Derek 2007-06-25 15:26:55 UTC
Yes, exporting the feed list would be very useful.  Surprised it's not already possible.
Comment 7 Sujee Maniyam 2007-07-13 20:42:37 UTC
This feature is missing in Amarok 1.4.6.  
Do we know if it is scheduled to be included in any near term release?
Comment 8 Mark Kretschmann 2007-07-13 22:21:09 UTC
On 13 Jul 2007 18:42:38 -0000, Sujee Sivasubramaniyam <kde@sujee.net> wrote:
> This feature is missing in Amarok 1.4.6.
> Do we know if it is scheduled to be included in any near term release?


Unfortunately it won't happen very soon. 1.4 is feature frozen (only
bugfixes allowed), and all current development happens in 2.0, which
is due October 2007 earliest. Realistically, it will be later.

Would be cool to have it in 2.0, but currently we are *very* busy
already, so any help is welcome.
Comment 9 Kuhn 2007-07-21 00:47:31 UTC
This feature would have another advantage: Some people intend to reinstall Linux on their computer completely. How should they otherwise save their personal content - like all (gr)own playlists ?
Comment 10 Robert Brown 2008-06-09 15:25:46 UTC
Import and export features are the sign of truly open software. A two year wait for this feature to be added is too long. Come on Amarok team - increase the priority of this task, please.
Comment 11 Lydia Pintscher 2008-06-09 16:10:36 UTC
@Kuhn: by copying their database and settings from .kde/share/apps/Amarok and .kde/share/config for example

@Robert: This is not going to be done in Amarok 1.4.x as it is in feature freeze. I therefore up the version to 2.0-svn. Podcast support has been worked on in Amarok 2 already and looks promising. No idea if you are already able to export them though.
Comment 12 Bart Cerneels 2008-08-24 11:21:04 UTC
@Robert Brown
To be able to grant you that request I will need:
a) 35.000 euro per year to work on Open Source projects full time
b) a project manager for my house-in-planning
or
c) patience from our users.

Thank you for either one of those 3.
Comment 13 Pratik Patel 2008-08-24 18:48:57 UTC
@Robert Brown
Banshee and Rhythmbox does not have this feature. Miro has this feature.
Comment 14 Rashad Tatum 2009-03-15 03:46:50 UTC
I recently started developing for Amarok (specifically on podcasts).  I will attempt to implement this feature.
Comment 15 Rashad Tatum 2009-03-15 03:47:29 UTC
I recently started developing for Amarok (specifically on podcasts).  I will attempt to implement this feature.
Comment 16 Rashad Tatum 2009-03-15 03:49:39 UTC
Please delete the duplicate of my comment.
Comment 17 Bart Cerneels 2009-11-23 15:48:59 UTC
Importing of OPML is implemented in v2.2.1-236-g247a935 and will be part of Amarok 2.2.2.
Still working on the exporting.
Comment 18 Frank Steinmetzger 2010-02-02 04:12:32 UTC
It’s good to hear that progress is being made in that area. But sadly it doesn’t help me and thousands (millions? *g*) of other users who still cling on to Amarok 1, because we can’t migrate our collection of podcast files (1,7 GB in my case). Can you tell me please if I should still uphold my hopes or is it perhaps already decided that there will definitely be no solution for this of any kind?

In the latter case I could try and put my (somewhat limited) SQL knowledge to use and write some kind of converter. Though I would need some time to get aqcuainted with the DB structures of both versions.

Cheers
Comment 19 Bart Cerneels 2010-02-02 08:48:35 UTC
(In reply to comment #18)
> It’s good to hear that progress is being made in that area. But sadly it
> doesn’t help me and thousands (millions? *g*) of other users who still cling on
> to Amarok 1, because we can’t migrate our collection of podcast files (1,7 GB
> in my case). Can you tell me please if I should still uphold my hopes or is it
> perhaps already decided that there will definitely be no solution for this of
> any kind?
> 
> In the latter case I could try and put my (somewhat limited) SQL knowledge to
> use and write some kind of converter. Though I would need some time to get
> aqcuainted with the DB structures of both versions.
> 
> Cheers

Hey Frank,
Could you please open a new bug report for this.

I wanted to do a 1.4-podcast importer included in the general database importer. But unfortunately there were more urgent tasks for me personally and no one else seemed interested. I figured Amarok 2 has been out for to long for anyone to still care. You are the first one that offers to implement it.

You can mail kde-devel@kde.org if you need help, please send a mail anyway that you are working on it. I'll give you pointers in a reply mail. The deadline for 2.2.3 inclusion is next Tuesday.

Talk to us in #amarok on freenode as well. My nick is Stecchino.

Good luck
Bart
Comment 20 Bart Cerneels 2010-07-20 23:34:15 UTC
*** Bug 231513 has been marked as a duplicate of this bug. ***
Comment 21 Bart Cerneels 2010-09-21 23:12:47 UTC
	A	 src/OpmlWriter.h	 [License: UNKNOWN]


	A	 src/OpmlWriter.cpp	 [License: UNKNOWN]


	A	 src/OpmlOutline.h	 [License: UNKNOWN]


	A	 src/OpmlOutline.cpp	 [License: UNKNOWN]

commit 2018f55d2df9dbbf53339c9594ab23743fefb3d7
Author: Bart Cerneels <bart.cerneels@kde.org>
Date:   Fri Apr 23 16:52:14 2010 +0200

    Add OPML export for local podcast subscription.
    
    BUG:126120

diff --git a/ChangeLog b/ChangeLog
index d204a3c..67d8742 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -4,6 +4,7 @@ Amarok ChangeLog
 
 VERSION NEXT
   FEATURES:
+    * OPML export for podcast subscriptions. (BR 126120)
     * New "Playlist Length" constraint for the APG, which allows you to specify
       the number of tracks in the playlist.  What was the "Playlist Length"
       constraint is now called "Playlist Duration".
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index d114881..d8325cc 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -667,7 +667,9 @@ set(amaroklib_LIB_SRCS
     browsers/filebrowser/FileView.cpp
     firstruntutorial/FirstRunTutorial.cpp
     firstruntutorial/FirstRunTutorialPage.cpp
+    OpmlOutline.cpp
     OpmlParser.cpp
+    OpmlWriter.cpp
     PaletteHandler.cpp
     PopupDropperFactory.cpp
     statemanagement/ApplicationController.cpp
diff --git a/src/OpmlOutline.cpp b/src/OpmlOutline.cpp
new file mode 100644
index 0000000..5a3d653
--- /dev/null
+++ b/src/OpmlOutline.cpp
@@ -0,0 +1,24 @@
+/****************************************************************************************
+ * Copyright (c) 2008 Nikolaj Hald Nielsen <nhn@kde.org>                                *
+ * Copyright (c) 2009-2010 Bart Cerneels <bart.cerneels@kde.org>                        *
+ *                                                                                      *
+ * This program is free software; you can redistribute it and/or modify it under        *
+ * the terms of the GNU General Public License as published by the Free Software        *
+ * Foundation; either version 2 of the License, or (at your option) any later           *
+ * version.                                                                             *
+ *                                                                                      *
+ * This program is distributed in the hope that it will be useful, but WITHOUT ANY      *
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A      *
+ * PARTICULAR PURPOSE. See the GNU General Public License for more details.             *
+ *                                                                                      *
+ * You should have received a copy of the GNU General Public License along with         *
+ * this program.  If not, see <http://www.gnu.org/licenses/>.                           *
+ ****************************************************************************************/
+
+#include "OpmlOutline.h"
+
+OpmlOutline::OpmlOutline( OpmlOutline *parent )
+        : m_parent( parent )
+        , m_hasChildren( false )
+{
+}
diff --git a/src/OpmlOutline.h b/src/OpmlOutline.h
new file mode 100644
index 0000000..8b64dc1
--- /dev/null
+++ b/src/OpmlOutline.h
@@ -0,0 +1,54 @@
+/****************************************************************************************
+ * Copyright (c) 2008 Nikolaj Hald Nielsen <nhn@kde.org>                                *
+ * Copyright (c) 2009-2010 Bart Cerneels <bart.cerneels@kde.org>                        *
+ *                                                                                      *
+ * This program is free software; you can redistribute it and/or modify it under        *
+ * the terms of the GNU General Public License as published by the Free Software        *
+ * Foundation; either version 2 of the License, or (at your option) any later           *
+ * version.                                                                             *
+ *                                                                                      *
+ * This program is distributed in the hope that it will be useful, but WITHOUT ANY      *
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A      *
+ * PARTICULAR PURPOSE. See the GNU General Public License for more details.             *
+ *                                                                                      *
+ * You should have received a copy of the GNU General Public License along with         *
+ * this program.  If not, see <http://www.gnu.org/licenses/>.                           *
+ ****************************************************************************************/
+
+#ifndef OPMLOUTLINE_H
+#define OPMLOUTLINE_H
+
+#include "amarok_export.h"
+
+#include <QMap>
+#include <QString>
+
+class AMAROK_EXPORT OpmlOutline
+{
+    public:
+        OpmlOutline( OpmlOutline *parent = 0 );
+        ~OpmlOutline() {}
+
+        OpmlOutline *parent() const { return m_parent; }
+        bool isRootItem() const { return m_parent == 0; }
+
+        QMap<QString,QString> attributes() const { return m_attributes; }
+        void addAttribute( const QString &key, const QString &value )
+                { m_attributes.insert( key, value ); }
+
+        QList<OpmlOutline *> children() const { return m_children; }
+        void setHasChildren( bool hasChildren ) { m_hasChildren = hasChildren; }
+        bool hasChildren() const { return m_hasChildren; }
+        void addChild( OpmlOutline *outline ) { m_children << outline; }
+        void addChildren( QList<OpmlOutline *> outlineList )
+                { m_children << outlineList; }
+
+    private:
+        OpmlOutline *m_parent;
+        QMap<QString,QString> m_attributes;
+
+        bool m_hasChildren;
+        QList<OpmlOutline *> m_children;
+};
+
+#endif // OPMLOUTLINE_H
diff --git a/src/OpmlParser.cpp b/src/OpmlParser.cpp
index 0656adb..017c6c2 100644
--- a/src/OpmlParser.cpp
+++ b/src/OpmlParser.cpp
@@ -26,13 +26,6 @@
 #include <KLocale>
 #include <threadweaver/Job.h>
 
-
-OpmlOutline::OpmlOutline( OpmlOutline *parent )
-        : m_parent( parent )
-        , m_hasChildren( false )
-{
-}
-
 const QString OpmlParser::OPML_MIME = "text/x-opml+xml";
 
 OpmlParser::OpmlParser( const QString &filename )
diff --git a/src/OpmlParser.h b/src/OpmlParser.h
index 338bea7..28c6c0c 100644
--- a/src/OpmlParser.h
+++ b/src/OpmlParser.h
@@ -19,6 +19,7 @@
 #define OPMLPARSER_H
 
 #include "amarok_export.h"
+#include "OpmlOutline.h"
 
 #include <threadweaver/Job.h>
 
@@ -27,34 +28,6 @@
 #include <QString>
 #include <QStringList>
 
-class AMAROK_EXPORT OpmlOutline
-{
-    public:
-        OpmlOutline( OpmlOutline *parent = 0 );
-        ~OpmlOutline();
-
-        OpmlOutline *parent() const { return m_parent; }
-        bool isRootItem() const { return m_parent == 0; }
-
-        QMap<QString,QString> attributes() const { return m_attributes; }
-        void addAttribute( const QString &key, const QString &value )
-                { m_attributes.insert( key, value ); }
-
-        QList<OpmlOutline *> children() const { return m_children; }
-        void setHasChildren( bool hasChildren ) { m_hasChildren = hasChildren; }
-        bool hasChildren() const { return m_hasChildren; }
-        void addChild( OpmlOutline *outline ) { m_children << outline; }
-        void addChildren( QList<OpmlOutline *> outlineList )
-                { m_children << outlineList; }
-
-    private:
-        OpmlOutline *m_parent;
-        QMap<QString,QString> m_attributes;
-
-        bool m_hasChildren;
-        QList<OpmlOutline *> m_children;
-};
-
 /**
 * Parser for OPML files.
 */
diff --git a/src/OpmlWriter.cpp b/src/OpmlWriter.cpp
new file mode 100644
index 0000000..ed78de7
--- /dev/null
+++ b/src/OpmlWriter.cpp
@@ -0,0 +1,74 @@
+/****************************************************************************************
+ * Copyright (c) 2010 Bart Cerneels <bart.cerneels@kde.org>                             *
+ *                                                                                      *
+ * This program is free software; you can redistribute it and/or modify it under        *
+ * the terms of the GNU General Public License as published by the Free Software        *
+ * Foundation; either version 2 of the License, or (at your option) any later           *
+ * version.                                                                             *
+ *                                                                                      *
+ * This program is distributed in the hope that it will be useful, but WITHOUT ANY      *
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A      *
+ * PARTICULAR PURPOSE. See the GNU General Public License for more details.             *
+ *                                                                                      *
+ * You should have received a copy of the GNU General Public License along with         *
+ * this program.  If not, see <http://www.gnu.org/licenses/>.                           *
+ ****************************************************************************************/
+
+#include "OpmlWriter.h"
+#include "core/support/Debug.h"
+
+#include <KUrl>
+
+OpmlWriter::OpmlWriter( const OpmlOutline *rootOutline, QIODevice *device )
+    : ThreadWeaver::Job()
+    , m_rootOutline( rootOutline )
+{
+    m_xmlWriter = new QXmlStreamWriter( device );
+}
+
+void
+OpmlWriter::run()
+{
+#define _x m_xmlWriter
+    _x->setAutoFormatting( true );
+    _x->writeStartDocument();
+    _x->writeStartElement( "opml" );
+    _x->writeAttribute( "version", "1.0" );
+    _x->writeStartElement( "head" );
+    //root outline is threated special, it's attributes will be the elements of <head>
+    QMapIterator<QString, QString> ai( m_rootOutline->attributes() ); //attributesIterator
+    while( ai.hasNext() )
+    {
+        ai.next();
+        _x->writeTextElement( ai.key(), ai.value() );
+    }
+    _x->writeEndElement(); // head
+    _x->writeStartElement( "body" );
+    foreach( const OpmlOutline *childOutline, m_rootOutline->children() )
+        writeOutline( childOutline );
+    _x->writeEndDocument(); //implicitly closes all open tags (opml & body)
+    emit result( 0 );
+}
+
+void
+OpmlWriter::writeOutline( const OpmlOutline *outline )
+{
+    bool hasChildren = outline->children().count() != 0;
+    if( hasChildren )
+        _x->writeStartElement( "outline" );
+    else
+        _x->writeEmptyElement( "outline" );
+    QMapIterator<QString, QString> ai( outline->attributes() ); // attributesIterator
+    while( ai.hasNext() )
+    {
+        ai.next();
+        _x->writeAttribute( ai.key(), ai.value() );
+    }
+
+    if( hasChildren )
+    {
+        foreach( const OpmlOutline *childOutline, outline->children() )
+            writeOutline( childOutline );
+        _x->writeEndElement(); // outline
+    }
+}
diff --git a/src/OpmlWriter.h b/src/OpmlWriter.h
new file mode 100644
index 0000000..c28e76d
--- /dev/null
+++ b/src/OpmlWriter.h
@@ -0,0 +1,55 @@
+/****************************************************************************************
+ * Copyright (c) 2010 Bart Cerneels <bart.cerneels@kde.org>                             *
+ *                                                                                      *
+ * This program is free software; you can redistribute it and/or modify it under        *
+ * the terms of the GNU General Public License as published by the Free Software        *
+ * Foundation; either version 2 of the License, or (at your option) any later           *
+ * version.                                                                             *
+ *                                                                                      *
+ * This program is distributed in the hope that it will be useful, but WITHOUT ANY      *
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A      *
+ * PARTICULAR PURPOSE. See the GNU General Public License for more details.             *
+ *                                                                                      *
+ * You should have received a copy of the GNU General Public License along with         *
+ * this program.  If not, see <http://www.gnu.org/licenses/>.                           *
+ ****************************************************************************************/
+
+#ifndef OPMLWRITER_H
+#define OPMLWRITER_H
+
+#include "OpmlOutline.h"
+
+#include <threadweaver/Job.h>
+#include <KUrl>
+
+#include <QXmlStreamWriter>
+
+class OpmlWriter : public ThreadWeaver::Job
+{
+    Q_OBJECT
+    public:
+        OpmlWriter( const OpmlOutline *rootOutline, QIODevice *device );
+
+        /**
+         * The function that starts the actual work. Inherited from ThreadWeaver::Job
+         * Note the work is performed in a separate thread
+         * @return Returns true on success and false on failure
+         */
+        void run();
+
+        QIODevice *device() { return m_xmlWriter->device(); }
+
+    signals:
+        /**
+         * Signal emmited when writing is complete.
+         */
+        void result( int error );
+
+    private:
+        void writeOutline( const OpmlOutline *outline );
+        const OpmlOutline *m_rootOutline;
+        KUrl m_fileUrl;
+        QXmlStreamWriter *m_xmlWriter;
+};
+
+#endif // OPMLWRITER_H
diff --git a/src/core-impl/podcasts/sql/SqlPodcastProvider.cpp b/src/core-impl/podcasts/sql/SqlPodcastProvider.cpp
index 7f4ff18..23a392b 100644
--- a/src/core-impl/podcasts/sql/SqlPodcastProvider.cpp
+++ b/src/core-impl/podcasts/sql/SqlPodcastProvider.cpp
@@ -31,10 +31,12 @@
 #include "core/collections/support/SqlStorage.h"
 #include "statusbar/StatusBar.h"
 #include "SvgHandler.h"
+#include "OpmlWriter.h"
 
 #include "ui_SqlPodcastProviderSettingsWidget.h"
 
 #include <KLocale>
+#include <KFileDialog>
 #include <KIO/CopyJob>
 #include <KIO/DeleteJob>
 #include <KIO/Job>
@@ -280,6 +282,13 @@ SqlPodcastProvider::providerActions()
         configureAction->setProperty( "popupdropper_svg_id", "configure" );
         connect( configureAction, SIGNAL( triggered() ), this, SLOT( slotConfigureProvider() ) );
         m_providerActions << configureAction;
+
+        QAction *exportOpmlAction = new QAction( KIcon( "document-export" ),
+                                                 i18n( "&Export to subscriptions to OPML file" ),
+                                                 this
+                                               );
+        connect( exportOpmlAction, SIGNAL(triggered()), SLOT(slotExportOpml()) );
+        m_providerActions << exportOpmlAction;
     }
 
     return m_providerActions;
@@ -294,6 +303,7 @@ SqlPodcastProvider::playlistActions( Playlists::PlaylistPtr playlist )
     if( sqlChannel.isNull() )
         return actions;
 
+    //TODO: add export OPML action for selected playlists only. Use the QAction::data() trick.
     if( m_configureChannelAction == 0 )
     {
         m_configureChannelAction = new QAction(
@@ -618,6 +628,52 @@ SqlPodcastProvider::slotConfigChanged()
 }
 
 void
+SqlPodcastProvider::slotExportOpml()
+{
+    OpmlOutline *rootOutline = new OpmlOutline();
+    //TODO: root OPML outline head
+
+    //TODO: folder outline support
+    foreach( SqlPodcastChannelPtr channel, m_channels )
+    {
+        OpmlOutline *channelOutline = new OpmlOutline( rootOutline );
+        #define addAttr( k, v ) channelOutline->addAttribute( k, v )
+        addAttr( "text", channel->title() );
+        addAttr( "xmlUrl", channel->url().url() );
+        rootOutline->addChild( channelOutline );
+    }
+
+    //TODO: add checkbox as widget to filedialog to include podcast settings.
+    KFileDialog fileDialog( KUrl( "kfiledialog:///podcast/amarok_podcasts.opml"), "*.opml",
+                            The::mainWindow() );
+    fileDialog.setMode( KFile::File );
+    fileDialog.setCaption( i18n( "Select file for OPML export") );
+    if( fileDialog.exec() != KDialog::Accepted )
+        return;
+
+    KUrl filePath = fileDialog.selectedUrl();
+
+    QFile *opmlFile = new QFile( filePath.toLocalFile(), this );
+    if( !opmlFile->open( QIODevice::WriteOnly | QIODevice::Truncate ) )
+    {
+        error() << "could not open OPML file " << filePath.url();
+        return;
+    }
+    OpmlWriter *opmlWriter = new OpmlWriter( rootOutline, opmlFile );
+    connect( opmlWriter, SIGNAL(result(int)), SLOT(slotOpmlWriterDone(int)) );
+    opmlWriter->run();
+}
+
+void
+SqlPodcastProvider::slotOpmlWriterDone( int result )
+{
+    OpmlWriter *writer = qobject_cast<OpmlWriter *>( QObject::sender() );
+    Q_ASSERT( writer );
+    writer->device()->close();
+    delete writer;
+}
+
+void
 SqlPodcastProvider::configureChannel( Podcasts::SqlPodcastChannelPtr sqlChannel )
 {
     if( !sqlChannel )
diff --git a/src/core-impl/podcasts/sql/SqlPodcastProvider.h b/src/core-impl/podcasts/sql/SqlPodcastProvider.h
index 09e9e2a..b650151 100644
--- a/src/core-impl/podcasts/sql/SqlPodcastProvider.h
+++ b/src/core-impl/podcasts/sql/SqlPodcastProvider.h
@@ -100,6 +100,7 @@ class SqlPodcastProvider : public Podcasts::PodcastProvider
         void slotDownloadProgress( KJob *job, unsigned long percent );
         void slotWriteTagsToFiles();
         void slotConfigChanged();
+        void slotExportOpml();
 
     signals:
         void totalPodcastDownloadProgress( int progress );
@@ -113,6 +114,7 @@ class SqlPodcastProvider : public Podcasts::PodcastProvider
                                                                const QString &description,
                                                                Podcasts::PodcastReader* reader );
         void slotStatusBarSorryMessage( const QString &message );
+        void slotOpmlWriterDone( int result );
 
     private:
         void configureProvider();
Comment 22 andreaswuest 2011-01-31 21:43:41 UTC
i have updated to version 2.4 of amarok (ubuntu packages) but i cannot find the export function! where is it supposed to be? i can only see the button for opml import. 
thanks in advance.
Comment 23 Bart Cerneels 2011-02-01 08:52:56 UTC
Created attachment 56726 [details]
screenshot of location export to OPML action.

The export action is specific to the SQL podcasts and so you'll find it as an action there. These actions show on the playlist-root items representing the sources of the content when you hover over them. Disable "Merged View" or you won't see them.

I'll attach a screenshot so you know what to look for.
Comment 24 andreaswuest 2011-02-01 20:29:06 UTC
hi Bart,
thanks for the screenshot. i finally found the export button :-)
Comment 25 bjoernv 2014-11-08 12:51:45 UTC
Created attachment 89502 [details]
No OPML export in Amarok 2.8 found
Comment 26 bjoernv 2014-11-08 12:55:07 UTC
The OPML export option (shown in screenshot from 2011-02-01) can not be found in Amarok 2.8 (screenshot from 2014-11-08) anymore. If there is no other location, please re-open this bug.
Comment 27 Myriam Schweingruber 2014-11-09 10:08:48 UTC
(In reply to bjoernv from comment #26)
> The OPML export option (shown in screenshot from 2011-02-01) can not be
> found in Amarok 2.8 (screenshot from 2014-11-08) anymore. If there is no
> other location, please re-open this bug.

No, please do not reopen such an old bug, which was actually a feature request, but file a new bug
Comment 28 Myriam Schweingruber 2014-11-09 10:11:15 UTC
FWIW: the OPML Import button is right there, on top of the podcast panel on the right, so nothing to report anway.
Comment 29 bjoernv 2014-11-09 20:09:46 UTC
No, Myriam. The OPML _import_ button is there, but there is not OPML export button.
Comment 30 Myriam Schweingruber 2014-11-10 01:00:14 UTC
Then please open a new report. Currently you can export to m3u, pls and xspf, just right-click on the subscription.
Comment 31 Dan Dart 2016-10-19 12:30:05 UTC
As I found no such report I opened one here: https://bugs.kde.org/show_bug.cgi?id=371192