Git commit 4fe719ff71c31e7bb1735519788b6cdb8844740d by Thomas Eschenbacher. Committed on 22/04/2017 at 19:14. Pushed by eschenbacher into branch 'master'.
new plugin: export to K3B project file M +5 -0 AUTHORS M +1 -0 CHANGES M +1 -0 CMakeLists.txt M +12 -0 LICENSES M +239 -1 doc/en/index.docbook M +1 -0 plugins/CMakeLists.txt A +43 -0 plugins/export_k3b/CMakeLists.txt A +95 -0 plugins/export_k3b/K3BExportDialog.cpp [License: GPL (v2+)] A +95 -0 plugins/export_k3b/K3BExportDialog.h [License: GPL (v2+)] A +789 -0 plugins/export_k3b/K3BExportPlugin.cpp [License: GPL (v2+)] A +179 -0 plugins/export_k3b/K3BExportPlugin.h [License: GPL (v2+)] A +125 -0 plugins/export_k3b/K3BExportWidget.cpp [License: GPL (v2+)] A +83 -0 plugins/export_k3b/K3BExportWidget.h [License: GPL (v2+)] A +204 -0 plugins/export_k3b/K3BExportWidgetBase.ui A +11 -0 plugins/export_k3b/kwaveplugin_export_k3b.desktop.in M +3 -6 plugins/saveblocks/SaveBlocksPlugin.cpp M +12 -0 scripts/screenshots.kwave https://commits.kde.org/kwave/4fe719ff71c31e7bb1735519788b6cdb8844740d diff --git a/AUTHORS b/AUTHORS index d113f274..588b3135 100644 --- a/AUTHORS +++ b/AUTHORS @@ -62,6 +62,11 @@ Sven-Steffen Arndt <[email protected]> minor contributors, copyright holders and others ================================================ +Sebastian Trueg <[email protected]>, +Gustavo Pichorim Boiko <[email protected]> +Michal Malek <[email protected]> + * parts of plugins/export_k3b/K3BExportPlugin.cpp + Aaron Holtzman <[email protected]> * libkwave/cpu_accel.cpp diff --git a/CHANGES b/CHANGES index d41d049a..e38d2fea 100644 --- a/CHANGES +++ b/CHANGES @@ -3,6 +3,7 @@ xx.xx.x [xxxx-xx-xx] * reduced flicker of position widget * bugfix: deleting labels per menu did not work + * new plugin: export to K3B project file 17.04.0 [2017-03-19] diff --git a/CMakeLists.txt b/CMakeLists.txt index 70b6c873..b509ab12 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -219,6 +219,7 @@ INCLUDE(GenerateExportHeader) INCLUDE(FeatureSummary) FIND_PACKAGE(KF5 REQUIRED COMPONENTS + Archive Completion Config ConfigWidgets diff --git a/LICENSES b/LICENSES index c20a07ac..56bda8a6 100644 --- a/LICENSES +++ b/LICENSES @@ -630,6 +630,18 @@ COMPLETE LIST OF FILES AND THEIR LICENSE plugins/debug/DebugPlugin.h GPL2+ plugins/debug/kwaveplugin_debug.desktop.in GPL2+ + plugins/export_k3b/CMakeLists.txt BSD (3 clause) + plugins/export_k3b/K3BExportDialog.cpp GPL2+ + plugins/export_k3b/K3BExportDialog.h GPL2+ + plugins/export_k3b/K3BExportPlugin.cpp GPL2+ / Sebastian Trueg <[email protected]>, + Gustavo Pichorim Boiko <[email protected]> + Michal Malek <[email protected]> + plugins/export_k3b/K3BExportPlugin.h GPL2+ + plugins/export_k3b/K3BExportWidget.cpp GPL2+ + plugins/export_k3b/K3BExportWidget.h GPL2+ + plugins/export_k3b/K3BExportWidegetBase.ui GPL2+ + plugins/export_k3b/kwaveplugin_export_k3b.desktop.in GPL2+ + plugins/goto/CMakeLists.txt BSD (3 clause) plugins/goto/GotoDialog.cpp GPL2+ plugins/goto/GotoDialog.h GPL2+ diff --git a/doc/en/index.docbook b/doc/en/index.docbook index 84f17b1d..4079b4fc 100644 --- a/doc/en/index.docbook +++ b/doc/en/index.docbook @@ -17,6 +17,7 @@ <!ENTITY url_flac "http://flac.sourceforge.net/"> <!ENTITY url_homepage "http://kwave.sourceforge.net/"> <!ENTITY url_id3lib "http://id3lib.sourceforge.net/"> + <!ENTITY url_k3b "http://www.k3b.org/"> <!ENTITY url_lame "http://lame.sourceforge.net/"> <!ENTITY url_levelmeter "https://web.archive.org/web/*/http://www.rikkus.info/esound-level-meter/"> <!ENTITY url_libaudiofile "http://www.68k.org/~michael/audiofile/"> @@ -147,6 +148,7 @@ <!ENTITY no-i18n-plugin_codec_ogg "codec_ogg"> <!ENTITY no-i18n-plugin_codec_wav "codec_wav"> <!ENTITY no-i18n-plugin_debug "debug"> + <!ENTITY no-i18n-plugin_export_k3b "export_k3b"> <!ENTITY no-i18n-plugin_fileinfo "fileinfo"> <!ENTITY no-i18n-plugin_goto "goto"> <!ENTITY no-i18n-plugin_insert_at "insert_at"> @@ -4456,6 +4458,9 @@ <indexdiv><title>d</title> <indexentry><primaryie><link linkend="plugin_sect_debug" endterm="plugin_title_debug"/></primaryie></indexentry> </indexdiv> + <indexdiv><title>e</title> + <indexentry><primaryie><link linkend="plugin_sect_export_k3b" endterm="plugin_title_export_k3b"/></primaryie></indexentry> + </indexdiv> <indexdiv><title>f</title> <indexentry><primaryie><link linkend="plugin_sect_fileinfo" endterm="plugin_title_fileinfo"/></primaryie></indexentry> </indexdiv> @@ -5355,6 +5360,227 @@ </variablelist> </sect1> + <!-- @PLUGIN@ export_k3b --> + <sect1 id="plugin_sect_export_k3b"><title id="plugin_title_export_k3b">&no-i18n-plugin_export_k3b; (Export to K3B Project)</title> + <screenshot> + <screeninfo>Screenshot</screeninfo> + <mediaobject> + <imageobject> + <imagedata fileref="kwave-plugin-export_k3b.png" format="PNG"/> + </imageobject> + <textobject> + <phrase>Screenshot of the K3B Export Plugin</phrase> + </textobject> + </mediaobject> + </screenshot> + <variablelist> + <varlistentry> + <term><emphasis role="bold">&i18n-plugin_lbl_internal_name;</emphasis></term> + <listitem><para><literal>&no-i18n-plugin_export_k3b;</literal></para></listitem> + </varlistentry> + <varlistentry> + <term><emphasis role="bold">&i18n-plugin_lbl_type;</emphasis></term> + <listitem><para>function</para></listitem> + </varlistentry> + <varlistentry> + <term><emphasis role="bold">&i18n-plugin_lbl_description;</emphasis></term> + <listitem> + <para> + Saves all sections between markers into a separate file and creates + a K3B project file. After having successfully written all files it + is possible to start <ulink url="&url_k3b;">K3B</ulink> and + burn the result to an audio CD. + This is useful for splitting a file with a recording that consists + of several parts, which are separated by labels, and then burn it + to an audio CD with multiple tracks, including CD text meta data + which is extracted from the descriptions of the labels. + </para> + <para> + (This plugin is internally using the + <link linkend="plugin_sect_saveblocks" endterm="plugin_title_saveblocks"/> + plugin.) + </para> + </listitem> + </varlistentry> + <varlistentry> + <term><emphasis role="bold">&i18n-plugin_lbl_parameters;</emphasis></term> + <listitem> + <variablelist> + <varlistentry> + <term><replaceable>filename</replaceable></term> + <listitem> + <para> + The name of the K3B project file, will be used as + base name for the exported file names. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><replaceable>pattern</replaceable></term> + <listitem> + <para> + A pattern that will be used for detecting + title and artist from the label at the start + of a section. + It supports the following wildcards which + will be replaced by the corresponding content + when creating the CD text meta data: + </para> + <informaltable frame='all'> + <tgroup cols='2'> + <thead> + <row> + <entry align='left'>wildcard</entry> + <entry align='left'>description</entry> + </row> + </thead> + <tbody> + <row> + <entry>&no-i18n-tag;<command>[%artist]</command></entry> + <entry> + Will be replaced with the artist that performed + the corresponding block or alternatively the author. + </entry> + </row> + <row> + <entry>&no-i18n-tag;<command>[%title]</command></entry> + <entry> + Will be replaced with the title of the block, which is taken + from the descriptive text of the label at the + <emphasis>start</emphasis> of the block. + If that text is empty it will fall back to the title + of the file (see file information item + "<link linkend="INF_NAME">Name</link>"). + If this also does not exist, it will fall back to the + base file name as described above. + </entry> + </row> + </tbody> + </tgroup> + </informaltable> + <para> + Example: <quote><command><literal>[%title] ([%artist])</literal></command></quote> + will detect author <quote><command><literal>Beethoven</literal></command></quote> and title + <quote><command><literal>Symphony No. 5</literal></command></quote> from the string + <quote><command><literal>Symphony No. 5 (Beethoven)</literal></command></quote>. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><replaceable>selection only</replaceable></term> + <listitem> + <para> + <informaltable frame='all'> + <tgroup cols='2'> + <thead> + <row> + <entry align='left'>value</entry> + <entry align='left'>description</entry> + </row> + </thead> + <tbody> + <row> + <entry>&no-i18n-tag;<command>0</command></entry> + <entry> + Save all sections of the whole file. + </entry> + </row> + <row> + <entry>&no-i18n-tag;<command>1</command></entry> + <entry> + Save only the sections that are within the + current selection. If nothing is selected, + the whole file will be saved. + </entry> + </row> + </tbody> + </tgroup> + </informaltable> + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><replaceable>export location</replaceable></term> + <listitem> + <para> + Determines where the blocks should be saved. + <informaltable frame='all'> + <tgroup cols='2'> + <thead> + <row> + <entry align='left'>value</entry> + <entry align='left'>description</entry> + </row> + </thead> + <tbody> + <row> + <entry>&no-i18n-tag;<command>0</command></entry> + <entry> + Save to the same directory as the + K3B project file. + </entry> + </row> + <row> + <entry>&no-i18n-tag;<command>1</command></entry> + <entry> + Save into a sub directory of the directory + of the K3B project file, using the K3B + project file name as base and appending + <quote><command><literal>.dir</literal></command></quote>. + </entry> + </row> + </tbody> + </tgroup> + </informaltable> + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><replaceable>overwrite policy</replaceable></term> + <listitem> + <para> + Determines where the numbering should start. + <informaltable frame='all'> + <tgroup cols='2'> + <thead> + <row> + <entry align='left'>value</entry> + <entry align='left'>description</entry> + </row> + </thead> + <tbody> + <row> + <entry>&no-i18n-tag;<command>0</command></entry> + <entry> + Always start with index 1, with the risk + of overwriting existing files. + </entry> + </row> + <row> + <entry>&no-i18n-tag;<command>1</command></entry> + <entry> + Continue after the index of the highest + index that already exists, this avoids + overwriting existing files. + </entry> + </row> + </tbody> + </tgroup> + </informaltable> + </para> + </listitem> + </varlistentry> + + </variablelist> + </listitem> + </varlistentry> + </variablelist> + </sect1> + <!-- @PLUGIN@ fileinfo --> <sect1 id="plugin_sect_fileinfo"><title id="plugin_title_fileinfo">&no-i18n-plugin_fileinfo; (File Info)</title> <screenshot> @@ -6618,7 +6844,7 @@ <row> <entry>&no-i18n-tag;<command>[%title]</command></entry> <entry> - Will be replaced with the title the block, which is taken + Will be replaced with the title of the block, which is taken from the descriptive text of the label at the <emphasis>start</emphasis> of the block. If that text is empty it will fall back to the title @@ -7722,6 +7948,18 @@ plugins/record/Record-PulseAudio.h </para> </listitem> + <listitem> + <para><emphasis role="bold"> + Sebastian Trueg <email>[email protected]</email>, + + Gustavo Pichorim Boiko <email>[email protected]</email>, + + Michal Malek <email>[email protected]</email> + </emphasis></para> + <para> + parts of plugins/export_k3b/K3BExportPlugin.cpp + </para> + </listitem> </itemizedlist> </para> </sect1> diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index e2ee8038..0a2d9de6 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -105,6 +105,7 @@ ADD_SUBDIRECTORY( codec_mp3 ) # needs libmad + id3lib + "lame" ADD_SUBDIRECTORY( codec_ogg ) # needs libogg and (libvorbis or libopus) ADD_SUBDIRECTORY( codec_wav ) # needs libaudiofile ADD_SUBDIRECTORY( debug ) +ADD_SUBDIRECTORY( export_k3b ) ADD_SUBDIRECTORY( fileinfo ) ADD_SUBDIRECTORY( goto ) ADD_SUBDIRECTORY( lowpass ) diff --git a/plugins/export_k3b/CMakeLists.txt b/plugins/export_k3b/CMakeLists.txt new file mode 100644 index 00000000..caee2064 --- /dev/null +++ b/plugins/export_k3b/CMakeLists.txt @@ -0,0 +1,43 @@ +############################################################################# +## Kwave - plugins/export_k3b/CMakeLists.txt +## ------------------- +## begin : Thu Mar 23 2017 +## copyright : (C) 2017 by Thomas Eschenbacher +## email : [email protected] +############################################################################# +# +############################################################################# +# # +# Redistribution and use in source and binary forms, with or without # +# modification, are permitted provided that the following conditions # +# are met: # +# # +# 1. Redistributions of source code must retain the above copyright # +# notice, this list of conditions and the following disclaimer. # +# 2. Redistributions in binary form must reproduce the above copyright # +# notice, this list of conditions and the following disclaimer in the # +# documentation and/or other materials provided with the distribution. # +# # +# For details see the accompanying cmake/COPYING-CMAKE-SCRIPTS file. # +# # +############################################################################# + +SET(plugin_export_k3b_LIB_SRCS + K3BExportDialog.cpp + K3BExportPlugin.cpp + K3BExportWidget.cpp +) + +SET(plugin_export_k3b_LIB_UI + K3BExportWidgetBase.ui +) + +SET(plugin_export_k3b_LIBS + KF5::Archive +) + +KWAVE_PLUGIN(export_k3b) + +############################################################################# +############################################################################# + diff --git a/plugins/export_k3b/K3BExportDialog.cpp b/plugins/export_k3b/K3BExportDialog.cpp new file mode 100644 index 00000000..47f276b5 --- /dev/null +++ b/plugins/export_k3b/K3BExportDialog.cpp @@ -0,0 +1,95 @@ +/*************************************************************************** + * K3BExportDialog.cpp - Extended KwaveFileDialog for exporting to K3B + * ------------------- + * begin : Thu Apr 13 2017 + * copyright : (C) 2017 by Thomas Eschenbacher + * email : [email protected] + ***************************************************************************/ + +/*************************************************************************** + * * + * 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. * + * * + ***************************************************************************/ + +#include "config.h" + +#include <QString> +#include <QUrl> + +#include <KComboBox> +#include <KUrlComboBox> + +#include "libkwave/String.h" + +#include "K3BExportDialog.h" +#include "K3BExportWidget.h" + +//*************************************************************************** +Kwave::K3BExportDialog::K3BExportDialog( + const QString &startDir, + const QString &filter, + QWidget *parent, + const QUrl &last_url, + const QString &last_ext, + QString &pattern, + bool selection_only, + bool have_selection, + Kwave::K3BExportPlugin::export_location_t export_location, + Kwave::K3BExportPlugin::overwrite_policy_t overwrite_policy +) + :Kwave::FileDialog(startDir, Kwave::FileDialog::SaveFile, filter, parent, + last_url, last_ext), + m_widget(new(std::nothrow) Kwave::K3BExportWidget( + this, pattern, selection_only, have_selection, + export_location, overwrite_policy + )) +{ + Q_ASSERT(m_widget); + setCustomWidget(m_widget); +} + +//*************************************************************************** +Kwave::K3BExportDialog::~K3BExportDialog() +{ + if (m_widget) delete m_widget; + m_widget = 0; +} + +// //*************************************************************************** +QString Kwave::K3BExportDialog::pattern() const +{ + Q_ASSERT(m_widget); + return (m_widget) ? m_widget->pattern() : _(""); +} + +//*************************************************************************** +bool Kwave::K3BExportDialog::selectionOnly() const +{ + Q_ASSERT(m_widget); + return (m_widget) ? m_widget->selectionOnly() : false; +} + +//*************************************************************************** +Kwave::K3BExportPlugin::export_location_t + Kwave::K3BExportDialog::exportLocation() const +{ + Q_ASSERT(m_widget); + return (m_widget) ? m_widget->exportLocation() : + Kwave::K3BExportPlugin::EXPORT_TO_SUB_DIR; +} + +//*************************************************************************** +Kwave::K3BExportPlugin::overwrite_policy_t + Kwave::K3BExportDialog::overwritePolicy() const +{ + Q_ASSERT(m_widget); + return (m_widget) ? m_widget->overwritePolicy() : + Kwave::K3BExportPlugin::USE_NEW_FILE_NAMES; +} + +//*************************************************************************** +//*************************************************************************** diff --git a/plugins/export_k3b/K3BExportDialog.h b/plugins/export_k3b/K3BExportDialog.h new file mode 100644 index 00000000..70805145 --- /dev/null +++ b/plugins/export_k3b/K3BExportDialog.h @@ -0,0 +1,95 @@ +/*************************************************************************** + * K3BExportDialog.h - Extended KwaveFileDialog for exporting to K3B + * ------------------- + * begin : Thu Apr 13 2017 + * copyright : (C) 2017 by Thomas Eschenbacher + * email : [email protected] + ***************************************************************************/ + +/*************************************************************************** + * * + * 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. * + * * + ***************************************************************************/ + +#ifndef K3B_EXPORT_DIALOG_H +#define K3B_EXPORT_DIALOG_H + +#include "config.h" + +#include <QObject> +#include <QString> +#include <QWidget> + +#include "K3BExportPlugin.h" +#include "libgui/FileDialog.h" + +class QUrl; + +namespace Kwave +{ + + class K3BExportWidget; + + class K3BExportDialog: public Kwave::FileDialog + { + Q_OBJECT + public: + + /** + * Constructor. + * @see KFileFialog + * @param startDir the start directory + * @param filter string with a file type filter + * @param parent the parent widget + * @param last_url the last used URL + * @param last_ext the last used extension (preset only) + * @param pattern the pattern used for detecting title and artist + * @param selection_only if true, save only the selection + * @param have_selection if true, there is a selection + * @param export_location where to export files with tracks + * @param overwrite_policy overwrite existing files or use a new name + */ + K3BExportDialog( + const QString &startDir, + const QString &filter, + QWidget *parent, + const QUrl &last_url, + const QString &last_ext, + QString &pattern, + bool selection_only, + bool have_selection, + Kwave::K3BExportPlugin::export_location_t export_location, + Kwave::K3BExportPlugin::overwrite_policy_t overwrite_policy + ); + + /** Destructor */ + virtual ~K3BExportDialog(); + + /** returns the title/artist detection pattern (as is, not escaped) */ + QString pattern() const; + + /** returns true if only the selection should be saved */ + bool selectionOnly() const; + + /** returns export location of the files of the tracks */ + Kwave::K3BExportPlugin::export_location_t exportLocation() const; + + /** returns the file overwrite policy */ + Kwave::K3BExportPlugin::overwrite_policy_t overwritePolicy() const; + + private: + + /** the widget with extra settings for K3B export */ + Kwave::K3BExportWidget *m_widget; + + }; +} + +#endif /* K3B_EXPORT_DIALOG_H */ + +//*************************************************************************** +//*************************************************************************** diff --git a/plugins/export_k3b/K3BExportPlugin.cpp b/plugins/export_k3b/K3BExportPlugin.cpp new file mode 100644 index 00000000..8e9054bb --- /dev/null +++ b/plugins/export_k3b/K3BExportPlugin.cpp @@ -0,0 +1,789 @@ +/************************************************************************* + * K3BExportPlugin.cpp - export of K3B project files + * ------------------- + * begin : Thu Apr 13 2017 + * copyright : (C) 2017 by Thomas Eschenbacher + * email : [email protected] + ***************************************************************************/ + +/*************************************************************************** + * * + * 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. * + * * + ***************************************************************************/ + +#include "config.h" + +#include <errno.h> + +#include <QBuffer> +#include <QByteArray> +#include <QDir> +#include <QDomDocument> +#include <QDomElement> +#include <QFileInfo> +#include <QIODevice> +#include <QMap> +#include <QProcess> +#include <QRegExp> +#include <QTextStream> + +#include <KLocalizedString> // for the i18n macro +#include <KZip> + +#include "libkwave/CodecManager.h" +#include "libkwave/Encoder.h" +#include "libkwave/FileInfo.h" +#include "libkwave/Label.h" +#include "libkwave/LabelList.h" +#include "libkwave/Logger.h" +#include "libkwave/MessageBox.h" +#include "libkwave/MetaDataList.h" +#include "libkwave/Parser.h" +#include "libkwave/Plugin.h" +#include "libkwave/PluginManager.h" +#include "libkwave/SignalManager.h" +#include "libkwave/String.h" +#include "libkwave/Utils.h" + +#include "K3BExportDialog.h" +#include "K3BExportPlugin.h" + +KWAVE_PLUGIN(export_k3b, K3BExportPlugin) + +/** mime type of K3B project files */ +#define K3B_PROJECT_MIME_TYPE "application/x-k3b" + +/** file suffix of K3B project files */ +#define K3B_FILE_SUFFIX _("*.k3b") + +/** number of digits to use for out files */ +#define OUTFILE_DIGITS 4 + +/** file name pattern for out files */ +#define OUTFILE_PATTERN (_("[%0") + _("%1nr]").arg(OUTFILE_DIGITS)) + +/** file suffix for out files */ +#define OUTFILE_SUFFIX _(".wav") + +//*************************************************************************** +Kwave::K3BExportPlugin::K3BExportPlugin(QObject *parent, + const QVariantList &args) + :Kwave::Plugin(parent, args), + m_url(), + m_pattern(), + m_selection_only(false), + m_export_location(EXPORT_TO_SUB_DIR), + m_overwrite_policy(USE_NEW_FILE_NAMES), + m_block_info() +{ +} + +//*************************************************************************** +Kwave::K3BExportPlugin::~K3BExportPlugin() +{ +} + +//*************************************************************************** +int Kwave::K3BExportPlugin::interpreteParameters(QStringList ¶ms) +{ + bool ok; + QString param; + + // evaluate the parameter list + if (params.count() != 5) + return -EINVAL; + + // the selected URL + m_url = QUrl::fromUserInput(Kwave::Parser::unescape(params[0])); + if (!m_url.isValid()) return -EINVAL; + + // label pattern + m_pattern = Kwave::Parser::unescape(params[1]); + + // selection only + param = params[2]; + int v = param.toInt(&ok); + Q_ASSERT(ok); + if (!ok) return -EINVAL; + m_selection_only = (v != 0); + + // export location + param = params[3]; + int where = param.toInt(&ok); + Q_ASSERT(ok); + if (!ok) return -EINVAL; + if ((where != EXPORT_TO_SAME_DIR) && + (where != EXPORT_TO_SUB_DIR)) return -EINVAL; + m_export_location = static_cast<export_location_t>(where); + + // overwrite policy + param = params[4]; + int overwrite = param.toInt(&ok); + Q_ASSERT(ok); + if (!ok) return -EINVAL; + if ((overwrite != OVERWRITE_EXISTING_FILES) && + (overwrite != USE_NEW_FILE_NAMES)) return -EINVAL; + m_overwrite_policy = static_cast<overwrite_policy_t>(overwrite); + + return 0; +} + +//*************************************************************************** +void Kwave::K3BExportPlugin::scanBlocksToSave(const QString &base, + sample_index_t selection_left, + sample_index_t selection_right) +{ + sample_index_t block_start; + sample_index_t block_end = 0; + + Kwave::LabelList labels(signalManager().metaData()); + Kwave::LabelListIterator it(labels); + Kwave::Label label = (it.hasNext()) ? it.next() : Kwave::Label(); + + // get the title of the whole file, in case that a block does not have + // an own title + FileInfo info(signalManager().metaData()); + QString file_title = info.get(INF_NAME).toString(); + QString file_artist = info.get(INF_AUTHOR).toString(); + + // fallback: if there is no INF_NAME either, fall back to the file + // name as last resort + if (!file_title.length()) file_title = base; + + m_block_info.clear(); + QString prev_title = file_title; + for (unsigned int index = 1; ; ++index) { + block_start = block_end; + block_end = (label.isNull()) ? signalLength() : label.pos(); + + QString block_title = (!label.isNull() && label.name().length()) ? + label.name() : prev_title; + + if ((block_end > selection_left) && (block_start <= selection_right)) { + BlockInfo block; + + // init and set reasonable defaults + block.m_index = index; + block.m_filename = QString(); + block.m_start = block_start; + block.m_length = block_end - block_start; + block.m_title = block_title; + block.m_artist = file_artist; + + // detect title and artist + detectBlockMetaData(block_title, m_pattern, block); + m_block_info.append(block); + + prev_title = block.m_title; + +// qDebug("#%d [%llu...%llu]", index, block_start, block_end); +// qDebug(" title = '%s'", DBG(block.m_title)); +// qDebug(" artist = '%s'", DBG(block.m_artist)); + } + + if (label.isNull()) break; + label = (it.hasNext()) ? it.next() : Kwave::Label(); + } +} + +//*************************************************************************** +QString Kwave::K3BExportPlugin::createFileName(const QString &pattern, + unsigned int index) +{ + QString name = pattern; + QString num = _("%1").arg(index, OUTFILE_DIGITS, 10, QLatin1Char('0')); + name.replace(OUTFILE_PATTERN, num); + name += OUTFILE_SUFFIX; + return name; +} + +//*************************************************************************** +bool Kwave::K3BExportPlugin::detectBlockMetaData( + const QString &text, + const QString &pattern, + Kwave::K3BExportPlugin::BlockInfo &block +) +{ + if (!pattern.length()) { + // auto detect -> try all known patterns + foreach (const QString &p, knownPatterns()) + if (detectBlockMetaData(text, p, block)) + return true; + return false; + } + + // list of placeholders and pointers to the resulting strings + QMap <QString, QString *> map_patterns; + map_patterns.insert(_("[%artist]"), &block.m_artist); + map_patterns.insert(_("[%title]"), &block.m_title); + + // try to find the placeholders within the pattern + // NOTE: we use a map because it will automatically be sorted (by pos) + QString pattern_esc = Kwave::Parser::escape(pattern); + QMap <int, QString *> map_result; + foreach (const QString &placeholder, map_patterns.keys()) { + QString placeholder_esc; + placeholder_esc = Kwave::Parser::escape(placeholder); + if (pattern_esc.contains(placeholder_esc)) { + const QString rx_string = _("(.+)"); + int pos = pattern.indexOf(placeholder); + pattern_esc.replace(placeholder_esc, rx_string); + map_result.insert(pos, map_patterns[placeholder]); + } + } + if (map_result.isEmpty()) + return false; // no placeholders found in the patterns + + // relax the pattern: turn single whitespace to one or more whitespaces + pattern_esc.replace(QRegExp(_("(\\\\\\s)+")), _("\\s+")); + + // try to match the pattern on the given text + QRegExp rx(pattern_esc, Qt::CaseInsensitive); + if (!rx.exactMatch(text.trimmed())) + return false; // does not match :-( + + // we found a match + // -> now map the results into the corresponding result strings + for (int index = 0; index < map_result.count(); ++index) { + QString value = rx.cap(index + 1).trimmed(); + if (value.length()) { + QString *result = map_result[map_result.keys()[index]]; + if (result) *result = value; + } + } + + return true; +} + +//*************************************************************************** +void Kwave::K3BExportPlugin::load(QStringList ¶ms) +{ + Q_UNUSED(params); + + QString menu_path = _("File/Save/%1").arg(i18nc( + "menu: /File/Save/Export to K3B Project...", + "Export to K3B Project..." + )); + emitCommand(_("menu(plugin:setup(export_k3b),%1%2)").arg( + menu_path).arg(_("/#group(@SIGNAL)"))); + emitCommand(_("menu(plugin:setup(export_k3b),%1%2)").arg( + menu_path).arg(_("/#icon(application-x-k3b)"))); +} + +//*************************************************************************** +QStringList *Kwave::K3BExportPlugin::setup(QStringList ¶ms) +{ + // try to interpret the previous parameters + interpreteParameters(params); + + sample_index_t selection_left = 0; + sample_index_t selection_right = 0; + selection(0, &selection_left, &selection_right, false); + + // enable the "selection only" checkbox only if there is something + // selected but not everything + bool selected_something = (selection_left != selection_right); + bool selected_all = ((selection_left == 0) && + (selection_right + 1 >= signalLength())); + bool enable_selection_only = selected_something && !selected_all; + + // show a "File / Save As..." dialog for the *.k3b file + QPointer<Kwave::K3BExportDialog> dialog = + new(std::nothrow) Kwave::K3BExportDialog( + _("kfiledialog:///kwave_export_k3b"), + K3B_FILE_SUFFIX + _("|") + i18nc( + "file type filter when exporting to K3B", + "K3B project file (*.k3b)" + ), + parentWidget(), + QUrl::fromUserInput(signalName()), + _("*.k3b"), + m_pattern, + m_selection_only, + enable_selection_only, + m_export_location, + m_overwrite_policy + ); + if (!dialog) return 0; + + dialog->setWindowTitle(description()); + if (dialog->exec() != QDialog::Accepted) { + delete dialog; + return 0; + } + + QStringList *list = new(std::nothrow) QStringList(); + Q_ASSERT(list); + if (!list) { + delete dialog; + return 0; + } + + // user has pressed "OK" + QUrl url = dialog->selectedUrl(); + if (url.isEmpty()) { + delete dialog; + delete list; + return 0; + } + + QString name = url.path(); + QFileInfo path(name); + + // add the correct extension if necessary + if (path.suffix() != K3B_FILE_SUFFIX.mid(2)) + url.setPath(name + K3B_FILE_SUFFIX.mid(1)); + + name = Kwave::Parser::escape(url.toString()); + QString pattern = Kwave::Parser::escape(dialog->pattern()); + int export_location = static_cast<int>(dialog->exportLocation()); + int overwrite_policy = static_cast<int>(dialog->overwritePolicy()); + bool selection_only = (enable_selection_only) ? + dialog->selectionOnly() : m_selection_only; + + *list << name; // url + *list << pattern; // pattern + *list << QString::number(selection_only); // selection only + *list << QString::number(export_location); // export location + *list << QString::number(overwrite_policy); // overwrite policy + + emitCommand(_("plugin:execute(export_k3b,") + + name + _(",") + pattern + _(",") + + QString::number(selection_only) + _(",") + + QString::number(export_location) + _(",") + + QString::number(overwrite_policy) + _(")") + ); + + if (dialog) delete dialog; + return list; +} + +//*************************************************************************** +/* + * taken from K3B, libk3b/projects/k3bdoc.cpp + * + * Copyright (C) 2003-2008 Sebastian Trueg <[email protected]> + * + */ +void Kwave::K3BExportPlugin::saveGeneralDocumentData(QDomElement *part) +{ + QDomDocument doc = part->ownerDocument(); + QDomElement mainElem = doc.createElement(_("general")); + + QDomElement propElem = doc.createElement(_("writing_mode")); + propElem.appendChild(doc.createTextNode(_("auto"))); + mainElem.appendChild(propElem); + + propElem = doc.createElement(_("dummy")); + propElem.setAttribute(_("activated"), _("no")); + mainElem.appendChild(propElem); + + propElem = doc.createElement(_("on_the_fly")); + propElem.setAttribute(_("activated"), _("true")); + mainElem.appendChild(propElem); + + propElem = doc.createElement(_("only_create_images")); + propElem.setAttribute(_("activated"), _("no")); + mainElem.appendChild(propElem); + + propElem = doc.createElement(_("remove_images")); + propElem.setAttribute(_("activated"), _("no")); + mainElem.appendChild(propElem); + + part->appendChild( mainElem ); +} + +//*************************************************************************** +/* + * taken from K3B, libk3b/projects/audiocd/k3baudiodoc.cpp + * + * Copyright (C) 2003-2008 Sebastian Trueg <[email protected]> + * Copyright (C) 2009 Gustavo Pichorim Boiko <[email protected]> + * Copyright (C) 2010 Michal Malek <[email protected]> + */ +void Kwave::K3BExportPlugin::saveDocumentData(QDomElement *docElem) +{ + #define GET_INF(inf) doc.createTextNode(info.get(inf).toString()) + + const Kwave::FileInfo info(signalManager().metaData()); + + QDomDocument doc = docElem->ownerDocument(); + saveGeneralDocumentData(docElem); + + // add normalize + QDomElement normalizeElem = doc.createElement(_("normalize")); + normalizeElem.appendChild(doc.createTextNode(_("no"))); + docElem->appendChild(normalizeElem); + + // add hide track + QDomElement hideFirstTrackElem = doc.createElement(_("hide_first_track")); + hideFirstTrackElem.appendChild(doc.createTextNode(_("no"))); + docElem->appendChild(hideFirstTrackElem); + + // save the audio cd ripping settings + // paranoia mode, read retries, and ignore read errors + // ------------------------------------------------------------ + QDomElement ripMain = doc.createElement(_("audio_ripping")); + docElem->appendChild(ripMain); + + QDomElement ripElem = doc.createElement(_("paranoia_mode")); + ripElem.appendChild(doc.createTextNode(_("0"))); + ripMain.appendChild(ripElem); + + ripElem = doc.createElement(_("read_retries")); + ripElem.appendChild(doc.createTextNode(_("0"))); + ripMain.appendChild(ripElem); + + ripElem = doc.createElement(_("ignore_read_errors")); + ripElem.appendChild(doc.createTextNode(_("no"))); + ripMain.appendChild(ripElem); + // ------------------------------------------------------------ + + // save disc cd-text + // ------------------------------------------------------------- + QDomElement cdTextMain = doc.createElement(_("cd-text")); + cdTextMain.setAttribute(_("activated"), _("yes")); + + QDomElement cdTextElem = doc.createElement(_("title")); + cdTextElem.appendChild(GET_INF(INF_NAME)); + cdTextMain.appendChild(cdTextElem); + + cdTextElem = doc.createElement(_("artist")); + cdTextElem.appendChild(GET_INF(INF_AUTHOR)); + cdTextMain.appendChild(cdTextElem); + + cdTextElem = doc.createElement(_("arranger")); + cdTextElem.appendChild(GET_INF(INF_TECHNICAN)); + cdTextMain.appendChild(cdTextElem); + + cdTextElem = doc.createElement(_("songwriter")); + cdTextElem.appendChild(GET_INF(INF_PERFORMER)); + cdTextMain.appendChild(cdTextElem); + + cdTextElem = doc.createElement(_("composer")); + cdTextElem.appendChild(GET_INF(INF_ORGANIZATION)); + cdTextMain.appendChild(cdTextElem); + + cdTextElem = doc.createElement(_("disc_id")); + cdTextElem.appendChild(GET_INF(INF_CD)); + cdTextMain.appendChild(cdTextElem); + + cdTextElem = doc.createElement(_("upc_ean")); + cdTextElem.appendChild(GET_INF(INF_ISRC)); + cdTextMain.appendChild(cdTextElem); + + cdTextElem = doc.createElement(_("message")); + cdTextElem.appendChild(GET_INF(INF_COMMENTS)); + cdTextMain.appendChild(cdTextElem); + + docElem->appendChild( cdTextMain ); + // ------------------------------------------------------------- + + // save the tracks + // ------------------------------------------------------------- + QDomElement contentsElem = doc.createElement(_("contents")); + + unsigned int index = 1; + foreach (const Kwave::K3BExportPlugin::BlockInfo &block, m_block_info) { + QString title = block.m_title; + QString artist = block.m_artist; + QString songwriter = QString(); + QString url = block.m_filename; + + QDomElement trackElem = doc.createElement(_("track")); + + // add sources + QDomElement sourcesParent = doc.createElement(_("sources")); + QDomElement sourceElem = doc.createElement(_("file")); + sourceElem.setAttribute(_("url"), url); + sourceElem.setAttribute(_("start_offset"), _("00:00:00")); + sourceElem.setAttribute(_("end_offset"), _("00:00:00")); + sourcesParent.appendChild(sourceElem); + trackElem.appendChild(sourcesParent); + + // index 0 + QDomElement index0Elem = doc.createElement(_("index0")); + index0Elem.appendChild(doc.createTextNode(QString::number(index))); + trackElem.appendChild(index0Elem); + + // add cd-text + cdTextMain = doc.createElement(_("cd-text")); + cdTextElem = doc.createElement(_("title")); + cdTextElem.appendChild(doc.createTextNode(title)); + cdTextMain.appendChild(cdTextElem); + + cdTextElem = doc.createElement(_("artist")); + cdTextElem.appendChild(doc.createTextNode(artist)); + cdTextMain.appendChild(cdTextElem); + + cdTextElem = doc.createElement(_("arranger")); + cdTextElem.appendChild(GET_INF(INF_TECHNICAN)); + cdTextMain.appendChild(cdTextElem); + + cdTextElem = doc.createElement(_("songwriter")); + cdTextElem.appendChild(doc.createTextNode(songwriter)); + cdTextMain.appendChild(cdTextElem ); + + cdTextElem = doc.createElement(_("composer")); + cdTextElem.appendChild(GET_INF(INF_ORGANIZATION)); + cdTextMain.appendChild(cdTextElem); + + cdTextElem = doc.createElement(_("isrc")); + cdTextElem.appendChild(GET_INF(INF_ISRC)); + cdTextMain.appendChild(cdTextElem); + + cdTextElem = doc.createElement(_("message")); + cdTextElem.appendChild(GET_INF(INF_COMMENTS)); + cdTextMain.appendChild(cdTextElem); + + trackElem.appendChild(cdTextMain); + + // add copy protection + QDomElement copyElem = doc.createElement(_("copy_protection")); + copyElem.appendChild(doc.createTextNode( + info.get(INF_COPYRIGHTED).toInt() ? _("yes") : _("no") + )); + trackElem.appendChild(copyElem); + + // add pre emphasis + copyElem = doc.createElement(_("pre_emphasis")); + copyElem.appendChild(doc.createTextNode(_("no"))); + trackElem.appendChild(copyElem); + + contentsElem.appendChild(trackElem); + index++; + } + // ------------------------------------------------------------- + + docElem->appendChild(contentsElem); +} + +//*************************************************************************** +int Kwave::K3BExportPlugin::start(QStringList ¶ms) +{ + qDebug("K3BExportPlugin::start()"); + + // interpret the parameters + int result = interpreteParameters(params); + if (result) return result; + + // check the output file + if (!m_url.isLocalFile()) + return -EINVAL; // sorry, KZip supports only local files + + // determine output directory and file name pattern + QString k3b_filename = m_url.path(); + QFileInfo fi(k3b_filename); + QString base = fi.completeBaseName(); + QString out_dir; + QString out_pattern; + if (m_export_location == Kwave::K3BExportPlugin::EXPORT_TO_SUB_DIR) { + // export to a subdir with the name "<filename>.dir" + out_dir = fi.absolutePath() + QDir::separator() + base + _(".dir"); + out_pattern = _("track-") + OUTFILE_PATTERN; + } else { + // use the same directory as the *.k3b file + out_dir = fi.absolutePath(); + out_pattern = base + _("-track-") + OUTFILE_PATTERN; + } + qDebug("out_dir = '%s'", DBG(out_dir)); + qDebug("out_pattern = '%s'", DBG(out_pattern)); + + // determine the selection settings + sample_index_t selection_left = 0; + sample_index_t selection_right = 0; + QList<unsigned int> tracks; + selection(&tracks, &selection_left, &selection_right, false); + + // check: only mono or stereo files are supported + if ((tracks.count() != 1) && (tracks.count() != 2)) { + qWarning("sorry, K3B can not handle %u tracks", tracks.count()); + Kwave::MessageBox::sorry(parentWidget(), i18n( + "Only mono and stereo files can be used for an audio CD. " + "You can either deselect some channels or export the file " + "in a different file format that supports mono and stereo " + "only (for example FLAC) and then try again." + )); + return -EINVAL; + } + + bool selected_something = (selection_left != selection_right); + bool selected_all = ( (selection_left == 0) && + ((selection_right + 1) >= signalLength()) ); + bool enable_selection_only = selected_something && !selected_all; + bool selection_only = enable_selection_only && m_selection_only; + if (!selection_only) { + selection_left = 0; + selection_right = signalLength() - 1; + } + + // create a list of blocks to save, but not yet the output file names + scanBlocksToSave(base, selection_left, selection_right); + unsigned int count = m_block_info.count(); + if (!count) + return -EINVAL; + + // find the start index of the file numbering + unsigned int first = 1; + if (m_overwrite_policy == Kwave::K3BExportPlugin::USE_NEW_FILE_NAMES) { + // use new files, find out the highest existing index + QString pat = out_pattern; + pat.replace(OUTFILE_PATTERN, _("*")); + pat += OUTFILE_SUFFIX; + + QDir dir(out_dir, pat); + QStringList files; + files = dir.entryList(); + + for (unsigned int i = first; i < (first + count); ++i) { + QString name = createFileName(out_pattern, i); + QRegExp rx(_("^(") + name + _(")$"), Qt::CaseInsensitive); + QStringList matches = files.filter(rx); + if (matches.count() > 0) first = i + 1; + } + qDebug("found first usable index -> %d", first); + } else { + // overwrite mode, always start at 1 + } + + // create the complete file names + for (unsigned int i = 0; i < count; ++i) { + m_block_info[i].m_filename = out_dir + QDir::separator() + + createFileName(out_pattern, first + i); + } + + result = saveBlocks(selection_only, out_dir, out_pattern); + if (result != 0) + return result; // aborted or failed -> do not create a k3b file + + result = saveK3BFile(k3b_filename); + if (result != 0) + return result; // aborted or failed -> do not ask about starting k3b + + if (Kwave::MessageBox::questionYesNo(parentWidget(), i18n( + "A K3B project file has been created and audio files have " + "been exported.\n" + "Should I start K3B and open the audio CD project now?" + )) == KMessageBox::Yes) { + // call k3b and pass the project file name (must be full path) + QStringList args; + args << k3b_filename; + if (!QProcess::startDetached(_("k3b"), args)) { + return -EIO; + } + } + + return result; +} + +//*************************************************************************** +int Kwave::K3BExportPlugin::saveBlocks(bool selection_only, + const QString &out_dir, + const QString &out_pattern) +{ + QString first_filename = Kwave::Parser::escapeForFileName( + QUrl::fromLocalFile(createFileName(out_pattern, 1)).toString()); + + // remember the original file info remove all unsupported/ properties, + // to avoid that the saveblocks plugin complains... + const Kwave::FileInfo orig_file_info(signalManager().metaData()); + Kwave::FileInfo file_info(orig_file_info); + QList<Kwave::FileProperty> unsupported_properties; + { + QString mimetype = Kwave::CodecManager::whatContains(m_url); + Kwave::Encoder *encoder = Kwave::CodecManager::encoder(mimetype); + if (encoder) { + unsupported_properties = encoder->unsupportedProperties( + file_info.properties().keys()); + delete encoder; + } + if (!unsupported_properties.isEmpty()) { + foreach (const Kwave::FileProperty &p, unsupported_properties) { + file_info.set(p, QVariant()); + } + } + } + + // make sure that the file uses 16 bits/sample only + file_info.setBits(16); + + signalManager().metaData().replace(Kwave::MetaDataList(file_info)); + + // call the saveblocks plugin and let it do the main work of exporting + // the *.wav files with all the tracks... + + QStringList params; + params << out_dir + QDir::separator() + first_filename; + params << Kwave::Parser::escape(out_pattern); + params << ((m_overwrite_policy == USE_NEW_FILE_NAMES) ? _("0") : _("1")); + params << (selection_only ? _("1") : _("0")); + int result = manager().executePlugin(_("saveblocks"), ¶ms); + + // restore the original file info + signalManager().metaData().replace(Kwave::MetaDataList(orig_file_info)); + + return result; +} + +//*************************************************************************** +int Kwave::K3BExportPlugin::saveK3BFile(const QString &k3b_filename) +{ + // create the K3B file + KZip zip(k3b_filename); + + bool ok = zip.open(QIODevice::WriteOnly); + if (!ok) return -EIO; + + // write the mime type + QByteArray app_type(K3B_PROJECT_MIME_TYPE); + zip.setCompression(KZip::NoCompression); + zip.setExtraField(KZip::NoExtraField); + zip.writeFile(_("mimetype"), app_type); + + // export file global data + QByteArray xml; + QBuffer out(&xml); + out.open(QIODevice::WriteOnly); + + // save the data in the document + QDomDocument xmlDoc(_("k3b_audio_project")); + + xmlDoc.appendChild(xmlDoc.createProcessingInstruction( + _("xml"), _("version=\"1.0\" encoding=\"UTF-8\"") + )); + QDomElement docElem = xmlDoc.createElement(_("k3b_audio_project")); + xmlDoc.appendChild(docElem); + saveDocumentData(&docElem); + QTextStream xmlStream(&out); + xmlDoc.save(xmlStream, 0); + + out.close(); + + zip.setCompression(KZip::NoCompression); + zip.setExtraField(KZip::NoExtraField); + zip.writeFile(_("maindata.xml"), xml.data()); + zip.close(); + + return 0; +} + +//*************************************************************************** +QStringList Kwave::K3BExportPlugin::knownPatterns() +{ + // list of all known detection patterns + QStringList patterns; + patterns << _("[%title] ([%artist])"); + patterns << _("[%title], [%artist]"); + patterns << _("[%artist]: [%title]"); + patterns << _("[%artist] - [%title]"); + return patterns; +} + +//*************************************************************************** +#include "K3BExportPlugin.moc" +//*************************************************************************** +//*************************************************************************** diff --git a/plugins/export_k3b/K3BExportPlugin.h b/plugins/export_k3b/K3BExportPlugin.h new file mode 100644 index 00000000..12137e82 --- /dev/null +++ b/plugins/export_k3b/K3BExportPlugin.h @@ -0,0 +1,179 @@ +/************************************************************************* + * K3BExportPlugin.h - export of K3B project files + * ------------------- + * begin : Thu Apr 13 2017 + * copyright : (C) 2017 by Thomas Eschenbacher + * email : [email protected] + ***************************************************************************/ + +/*************************************************************************** + * * + * 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. * + * * + ***************************************************************************/ + +#ifndef K3B_EXPORT_PLUGIN_H +#define K3B_EXPORT_PLUGIN_H + +#include "config.h" + +#include <QObject> +#include <QStringList> +#include <QUrl> +#include <QVariantList> +#include <QVector> + +#include "libkwave/Plugin.h" + +class QDomElement; + +namespace Kwave +{ + + class K3BExportPlugin: public Kwave::Plugin + { + Q_OBJECT + public: + + typedef enum { + EXPORT_TO_SAME_DIR = 0, + EXPORT_TO_SUB_DIR = 1 + } export_location_t ; + + typedef enum { + OVERWRITE_EXISTING_FILES = 0, + USE_NEW_FILE_NAMES = 1 + } overwrite_policy_t; + + /** + * Constructor + * @param parent reference to our plugin manager + * @param args argument list [unused] + */ + K3BExportPlugin(QObject *parent, const QVariantList &args); + + /** Destructor */ + virtual ~K3BExportPlugin(); + + /** @see Kwave::Plugin::load() */ + virtual void load(QStringList ¶ms); + + /** + * Normally this method is used to set up all necessary parameters + * for executing the plugin. This plugin uses it for performing + * actions in the context of the GUI thread. + * + * @param params some parameters + * @return string list with parameters or null pointer + */ + virtual QStringList *setup(QStringList ¶ms); + + /** + * Saves the K3B project file, using the settings made in "setup()" + * @see Kwave::Plugin::start() + */ + virtual int start(QStringList ¶ms); + + /** returns a list of all known detection patterns */ + static QStringList knownPatterns(); + + protected: + + typedef struct { + unsigned int m_index; /**< track index [1...N] */ + QString m_filename; /**< file name for saving */ + sample_index_t m_start; /**< start of the block [samples] */ + sample_index_t m_length; /**< length of the block [samples] */ + QString m_title; /**< title of the block */ + QString m_artist; /**< artist of the song */ + } BlockInfo; + + /** reads values from the parameter list */ + int interpreteParameters(QStringList ¶ms); + + /** + * determines the blocks which should be saved, including + * start position, length and title. + * @param base the base name, without indices, extension etc... + * @param selection_left index of the first sample + * @param selection_right index of the last sample + */ + void scanBlocksToSave(const QString &base, + sample_index_t selection_left, + sample_index_t selection_right); + + /** + * create a filename (with extension) out of a given name pattern + * and index + * @param pattern the pattern for creating the filename + * @param index the index of the current file + * @return the name of the file + */ + QString createFileName(const QString &pattern, unsigned int index); + + /** + * detects the meta data of a block from splitting the description + * text of a label + * @param text the description of a label + * @param pattern a pattern describing the format of the text + * @param block a BlockInfo structure that receives artist and title + * @return true if the pattern did match, false otherwise + */ + bool detectBlockMetaData(const QString &text, + const QString &pattern, + BlockInfo &block); + + /** save the "general" section */ + void saveGeneralDocumentData(QDomElement *part); + + /** save the K3B project document data */ + void saveDocumentData(QDomElement *docElem); + + /** + * save the blocks through the saveblocks plugin + * @param selection_only if true, save only the selection + * @param out_dir output directory for saving the blocks + * @param out_pattern the pattern for creating the block filenames + * @return zero if succeeded, or error code if failed + */ + int saveBlocks(bool selection_only, + const QString &out_dir, + const QString &out_pattern); + + /** + * save the *.k3b file + * @param k3b_filename path to the *.k3b file + * @return zero if succeeded, or error code if failed + */ + int saveK3BFile(const QString &k3b_filename); + + private: + + /** the URL of the project file */ + QUrl m_url; + + /** pattern for detecting title and artist */ + QString m_pattern; + + /** if true, export only the selected range */ + bool m_selection_only; + + /** where to export the files of the tracks, subdir, same dir, ... */ + export_location_t m_export_location; + + /** overwrite existing files or create a new file name */ + overwrite_policy_t m_overwrite_policy; + + /** list of all blocks to save */ + QVector<BlockInfo> m_block_info; + }; +} + +#endif /* K3B_EXPORT_PLUGIN_H */ + +//*************************************************************************** +//*************************************************************************** + diff --git a/plugins/export_k3b/K3BExportWidget.cpp b/plugins/export_k3b/K3BExportWidget.cpp new file mode 100644 index 00000000..d2aae300 --- /dev/null +++ b/plugins/export_k3b/K3BExportWidget.cpp @@ -0,0 +1,125 @@ +/*************************************************************************** + * K3BExportWidget.cpp - widget for K3B export options in the file open dlg + * ------------------- + * begin : Thu Apr 13 2017 + * copyright : (C) 2017 by Thomas Eschenbacher + * email : [email protected] + ***************************************************************************/ + +/*************************************************************************** + * * + * 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. * + * * + ***************************************************************************/ + +#include "config.h" + +#include <QCheckBox> +#include <QLineEdit> + +#include <KComboBox> + +#include "libkwave/String.h" + +#include "K3BExportWidget.h" + +//*************************************************************************** +Kwave::K3BExportWidget::K3BExportWidget( + QWidget *parent, + QString &pattern, + bool selection_only, + bool have_selection, + Kwave::K3BExportPlugin::export_location_t export_location, + Kwave::K3BExportPlugin::overwrite_policy_t overwrite_policy +) + :QWidget(parent), Ui::K3BExportWidgetBase() +{ + setupUi(this); + + cbLabelPattern->addItem(i18nc( + "default entry of the list of placeholder patterns in " + "the K3B export plugin (used for detecting title and artist " + "from a label description)", + "(auto detect)" + )); + foreach (const QString &p, Kwave::K3BExportPlugin::knownPatterns()) + cbLabelPattern->addItem(p); + + Q_ASSERT(cbLabelPattern); + if (pattern.trimmed().length()) + cbLabelPattern->setCurrentText(pattern.trimmed()); + else + cbLabelPattern->setCurrentIndex(0); + + // the "selection only" checkbox + Q_ASSERT(chkSelectionOnly); + if (have_selection) { + // we have a selection + chkSelectionOnly->setEnabled(true); + chkSelectionOnly->setChecked(selection_only); + } else { + // no selection -> force it to "off" + chkSelectionOnly->setEnabled(false); + chkSelectionOnly->setChecked(false); + } + + Q_ASSERT(cbExportLocation); + cbExportLocation->setCurrentIndex(static_cast<int>(export_location)); + + Q_ASSERT(cbOverwritePolicy); + cbOverwritePolicy->setCurrentIndex(static_cast<int>(overwrite_policy)); +} + +//*************************************************************************** +Kwave::K3BExportWidget::~K3BExportWidget() +{ +} + +//*************************************************************************** +QString Kwave::K3BExportWidget::pattern() const +{ + Q_ASSERT(cbLabelPattern); + if (!cbLabelPattern) return QString(); + + // special handling: the first entry in the list is the default pattern + // (which is "auto-detect") -> map this to empty pattern + QString p = cbLabelPattern->currentText().trimmed(); + if (p == cbLabelPattern->itemText(0)) return QString(); + + return p; +} + +//*************************************************************************** +bool Kwave::K3BExportWidget::selectionOnly() const +{ + Q_ASSERT(chkSelectionOnly); + return (chkSelectionOnly) ? chkSelectionOnly->isChecked() : false; +} + +//*************************************************************************** +Kwave::K3BExportPlugin::export_location_t + Kwave::K3BExportWidget::exportLocation() const +{ + Q_ASSERT(cbExportLocation); + return static_cast<Kwave::K3BExportPlugin::export_location_t>( + (cbExportLocation) ? + cbExportLocation->currentIndex() : 0 + ); +} + +//*************************************************************************** +Kwave::K3BExportPlugin::overwrite_policy_t + Kwave::K3BExportWidget::overwritePolicy() const +{ + Q_ASSERT(cbOverwritePolicy); + return static_cast<Kwave::K3BExportPlugin::overwrite_policy_t>( + (cbOverwritePolicy) ? + cbOverwritePolicy->currentIndex() : 0 + ); +} + +//*************************************************************************** +//*************************************************************************** diff --git a/plugins/export_k3b/K3BExportWidget.h b/plugins/export_k3b/K3BExportWidget.h new file mode 100644 index 00000000..5495e89b --- /dev/null +++ b/plugins/export_k3b/K3BExportWidget.h @@ -0,0 +1,83 @@ +/*************************************************************************** + * K3BExportWidget.h - widget for K3B export options in the file open dlg + * ------------------- + * begin : Thu Apr 13 2017 + * copyright : (C) 2017 by Thomas Eschenbacher + * email : [email protected] + ***************************************************************************/ + +/*************************************************************************** + * * + * 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. * + * * + ***************************************************************************/ + +#ifndef K3B_EXPORT_WIDGET_H +#define K3B_EXPORT_WIDGET_H + +#include <QWidget> + +#include "K3BExportPlugin.h" +#include "ui_K3BExportWidgetBase.h" + +namespace Kwave +{ + class K3BExportWidget: public QWidget, public Ui::K3BExportWidgetBase + { + Q_OBJECT + public: + + /** + * Constructor + * @param widget pointer to the parent widget + * @param pattern the pattern used for detecting title and artist + * @param selection_only if true, save only the selection + * @param have_selection if true, there is a selection + * @param export_location where to export files with tracks + * @param overwrite_policy overwrite existing files or use a new name + */ + K3BExportWidget( + QWidget *widget, + QString &pattern, + bool selection_only, + bool have_selection, + Kwave::K3BExportPlugin::export_location_t export_location, + Kwave::K3BExportPlugin::overwrite_policy_t overwrite_policy + ); + + /** Destructor */ + virtual ~K3BExportWidget(); + + /** @see KPreviewWidgetBase::showPreview() */ + virtual void showPreview(const QUrl &url) + { + Q_UNUSED(url); + } + + /** @see KPreviewWidgetBase::clearPreview */ + virtual void clearPreview() + { + } + + /** returns the title/artist detection pattern (as is, not escaped) */ + QString pattern() const; + + /** returns true if only the selection should be saved */ + bool selectionOnly() const; + + /** returns export location of the files of the tracks */ + Kwave::K3BExportPlugin::export_location_t exportLocation() const; + + /** returns the file overwrite policy */ + Kwave::K3BExportPlugin::overwrite_policy_t overwritePolicy() const; + + }; +} + +#endif /* K3B_EXPORT_WIDGET_H */ + +//*************************************************************************** +//*************************************************************************** diff --git a/plugins/export_k3b/K3BExportWidgetBase.ui b/plugins/export_k3b/K3BExportWidgetBase.ui new file mode 100644 index 00000000..5569473f --- /dev/null +++ b/plugins/export_k3b/K3BExportWidgetBase.ui @@ -0,0 +1,204 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>K3BExportWidgetBase</class> + <widget class="QWidget" name="K3BExportWidgetBase"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>410</width> + <height>219</height> + </rect> + </property> + <property name="sizePolicy"> + <sizepolicy hsizetype="Minimum" vsizetype="Minimum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <property name="spacing"> + <number>20</number> + </property> + <property name="margin"> + <number>0</number> + </property> + <item> + <widget class="QGroupBox" name="grpInputSettings"> + <property name="title"> + <string>Input Settings</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout_2"> + <property name="margin"> + <number>10</number> + </property> + <item> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <widget class="QLabel" name="lblLabelFormat"> + <property name="text"> + <string>Format of Labels:</string> + </property> + <property name="wordWrap"> + <bool>false</bool> + </property> + </widget> + </item> + <item> + <widget class="KComboBox" name="cbLabelPattern"> + <property name="sizePolicy"> + <sizepolicy hsizetype="MinimumExpanding" vsizetype="Minimum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="whatsThis"> + <string><html><head/><body><p>Select a pattern for detecting title and artist from the text of a label.<br/>It currently understands the following placeholders:</p><ul style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Courier New,courier'; font-weight:600;">[%title] </span>title of the song</li><li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Courier New,courier'; font-weight:600;">[%artist]</span> name of the artist</li></ul></body></html></string> + </property> + <property name="editable"> + <bool>true</bool> + </property> + <property name="duplicatesEnabled"> + <bool>false</bool> + </property> + </widget> + </item> + </layout> + </item> + <item> + <widget class="QCheckBox" name="chkSelectionOnly"> + <property name="sizePolicy"> + <sizepolicy hsizetype="MinimumExpanding" vsizetype="Minimum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="whatsThis"> + <string>If checked, <b>save only the blocks that overlap with the current selection</b>, otherwise the whole file. <br><i>Please note that this option is disabled if nothing is selected or the selection already covers the whole file.</i></string> + </property> + <property name="text"> + <string>Use Selection only</string> + </property> + <property name="checked"> + <bool>true</bool> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QGroupBox" name="grpOutputSettings"> + <property name="title"> + <string>Output Settings</string> + </property> + <layout class="QGridLayout" name="gridLayout_2"> + <property name="leftMargin"> + <number>10</number> + </property> + <property name="topMargin"> + <number>10</number> + </property> + <property name="bottomMargin"> + <number>10</number> + </property> + <item row="0" column="0"> + <widget class="QLabel" name="lblExportLocation"> + <property name="text"> + <string>Export Location:</string> + </property> + <property name="wordWrap"> + <bool>false</bool> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="KComboBox" name="cbExportLocation"> + <property name="sizePolicy"> + <sizepolicy hsizetype="MinimumExpanding" vsizetype="Minimum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="whatsThis"> + <string>Specify where to place the exported sound files which are used for the tracks of the audio CD.</string> + </property> + <property name="duplicatesEnabled"> + <bool>false</bool> + </property> + <item> + <property name="text"> + <string>Same Directory as K3B File</string> + </property> + </item> + <item> + <property name="text"> + <string>Sub Directory</string> + </property> + </item> + </widget> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="lblOverwritePolicy"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Minimum" vsizetype="Minimum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Overwrite Policy:</string> + </property> + <property name="wordWrap"> + <bool>false</bool> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="KComboBox" name="cbOverwritePolicy"> + <property name="sizePolicy"> + <sizepolicy hsizetype="MinimumExpanding" vsizetype="Minimum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="whatsThis"> + <string>Select whether existing files are overwritten or new file names are used when exporting the files to use as tracks of the audio CD.</string> + </property> + <property name="duplicatesEnabled"> + <bool>false</bool> + </property> + <item> + <property name="text"> + <string>Overwrite Existing Files</string> + </property> + </item> + <item> + <property name="text"> + <string>Use new File Names</string> + </property> + </item> + </widget> + </item> + </layout> + </widget> + </item> + </layout> + </widget> + <layoutdefault spacing="10" margin="10"/> + <customwidgets> + <customwidget> + <class>KComboBox</class> + <extends>QComboBox</extends> + <header>kcombobox.h</header> + </customwidget> + </customwidgets> + <tabstops> + <tabstop>cbLabelPattern</tabstop> + <tabstop>chkSelectionOnly</tabstop> + <tabstop>cbExportLocation</tabstop> + <tabstop>cbOverwritePolicy</tabstop> + </tabstops> + <resources/> + <connections/> +</ui> diff --git a/plugins/export_k3b/kwaveplugin_export_k3b.desktop.in b/plugins/export_k3b/kwaveplugin_export_k3b.desktop.in new file mode 100644 index 00000000..cfe7c2c6 --- /dev/null +++ b/plugins/export_k3b/kwaveplugin_export_k3b.desktop.in @@ -0,0 +1,11 @@ +[Desktop Entry] +Encoding=UTF-8 +Name=K3B Project Export +Name[x-test]=xxK3B Project Exportxx +Type=Service +ServiceTypes=Kwave/Plugin +X-KDE-PluginInfo-Author=Thomas Eschenbacher +X-KDE-PluginInfo-Name=export_k3b +X-KDE-PluginInfo-Version=@KWAVE_VERSION@:2.3 +X-KDE-PluginInfo-License=GPL-2.0+ +X-KDE-PluginInfo-EnabledByDefault=true diff --git a/plugins/saveblocks/SaveBlocksPlugin.cpp b/plugins/saveblocks/SaveBlocksPlugin.cpp index 075e58e1..c2f76808 100644 --- a/plugins/saveblocks/SaveBlocksPlugin.cpp +++ b/plugins/saveblocks/SaveBlocksPlugin.cpp @@ -202,14 +202,11 @@ int Kwave::SaveBlocksPlugin::start(QStringList ¶ms) selection(0, &selection_left, &selection_right, false); bool selected_something = (selection_left != selection_right); - bool selected_all = ((selection_left == 0) && - (selection_right+1 >= signalLength())); + bool selected_all = ( (selection_left == 0) && + ((selection_right + 1) >= signalLength()) ); bool enable_selection_only = selected_something && !selected_all; bool selection_only = enable_selection_only && m_selection_only; - - if (selection_only) { - selection(0, &selection_left, &selection_right, true); - } else { + if (!selection_only) { selection_left = 0; selection_right = signalLength() - 1; } diff --git a/scripts/screenshots.kwave b/scripts/screenshots.kwave index 0de3ff83..5fcfab99 100644 --- a/scripts/screenshots.kwave +++ b/scripts/screenshots.kwave @@ -173,6 +173,18 @@ plugin-codec_mp3: sync() # +# screenshot of the K3B export setup dialog +# +plugin-export_k3b: + selectall() + select_track:off(1) + delayed(500,window:screenshot(Kwave::K3BExportDialog, /var/tmp/screenshots/${LANG}/kwave-plugin-export_k3b.png)) + delayed(100,window:close(Kwave::K3BExportDialog)) + plugin:setup(export_k3b) + sync() + select_track:on(1) + +# # screenshot of the File Info dialog # plugin-fileinfo:
