Git commit 5c8f85ca14658d7531ac4d05aae0a5352b1303e0 by Urs Fleisch. Committed on 14/04/2020 at 06:42. Pushed by ufleisch into branch 'master'.
Script to apply English title capitalization to tags BUG: 419890 M +5 -0 doc/en/index.docbook M +2 -1 src/core/config/configstore.cpp M +6 -0 src/core/config/useractionsconfig.cpp M +1 -0 src/qml/CMakeLists.txt A +125 -0 src/qml/script/TitleCase.qml [License: LGPL] https://invent.kde.org/kde/kid3/commit/5c8f85ca14658d7531ac4d05aae0a5352b1303e0 diff --git a/doc/en/index.docbook b/doc/en/index.docbook index cf3b0727..87da30e7 100644 --- a/doc/en/index.docbook +++ b/doc/en/index.docbook @@ -4455,6 +4455,11 @@ Helps to find the encoding of ID3v1 tags by showing the tags of the current file in all available character encodings. </para></listitem> <listitem><para> +<guilabel>English Title Case</guilabel> +(<filename>TitleCase.qml</filename>): +Formats text in the tags to English title case. +</para></listitem> +<listitem><para> <guilabel>Export CSV</guilabel> (<filename>ExportCsv.qml</filename>): Export recursively all tags of all files to a CSV file. diff --git a/src/core/config/configstore.cpp b/src/core/config/configstore.cpp index 6fc7a374..84e7730b 100644 --- a/src/core/config/configstore.cpp +++ b/src/core/config/configstore.cpp @@ -37,8 +37,9 @@ namespace { * 2: 3.2 * 3: 3.3 * 4: 3.7 + * 5: 3.8.3 */ -const int CONFIG_VERSION = 4; +const int CONFIG_VERSION = 5; } diff --git a/src/core/config/useractionsconfig.cpp b/src/core/config/useractionsconfig.cpp index 775da5d0..646d793e 100644 --- a/src/core/config/useractionsconfig.cpp +++ b/src/core/config/useractionsconfig.cpp @@ -245,6 +245,12 @@ void UserActionsConfig::setDefaultUserActions(bool upgradeOnly) QLatin1String("@qml %{qmlpath}/script/ExportPlaylist.qml"), false, true)); } + if (!upgradeOnly || ConfigStore::getConfigVersion() < 5) { + m_contextMenuCommands.push_back( + UserActionsConfig::MenuCommand( + QLatin1String("English Title Case"), + QLatin1String("@qml %{qmlpath}/script/TitleCase.qml"))); + } #endif } diff --git a/src/qml/CMakeLists.txt b/src/qml/CMakeLists.txt index 68fe922d..04114754 100644 --- a/src/qml/CMakeLists.txt +++ b/src/qml/CMakeLists.txt @@ -28,6 +28,7 @@ if(NOT HAVE_QMLDIR_IN_QRC) script/EmbedAlbumArt.qml script/EmbedLyrics.qml script/ExportPlaylist.qml + script/TitleCase.qml ) if(BUILD_QML_APP) # Only used to add the QML sources to the project. diff --git a/src/qml/script/TitleCase.qml b/src/qml/script/TitleCase.qml new file mode 100644 index 00000000..da047dc0 --- /dev/null +++ b/src/qml/script/TitleCase.qml @@ -0,0 +1,125 @@ +/** + * \file TitleCase.qml + * Use English title case in certain tag frames. + * + * \b Project: Kid3 + * \author Urs Fleisch + * \date 12 Apr 2020 + * + * Copyright (C) 2020 Urs Fleisch + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; version 3. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +import Kid3 1.1 + +Kid3Script { + onRun: { + var consideredTags = [Frame.Tag_1, Frame.Tag_2, Frame.Tag_3] + var consideredFrames = ["title", "album"] + + var small = "(a|an|and|as|at|but|by|en|for|if|in|of|on|or|the|to|v[.]?|via|vs[.]?)"; + var punct = "([!\"#$%&'()*+,./:;<=>?@[\\\\\\]^_`{|}~-]*)"; + // This array only contains words which cannot be handled by the all lower + // and upper case handling at the end of the toTitleCase() function. + var unmodifiableWords = ["Pi-hsien"]; + + /* + * Title Caps + * + * Adapted for audio tag use case by Urs Fleisch - 12 Apr 2020 + * Ported to JavaScript By John Resig - http://ejohn.org/ - 21 May 2008 + * Original by John Gruber - http://daringfireball.net/ - 10 May 2008 + * License: http://www.opensource.org/licenses/mit-license.php + */ + function toTitleCase(title) { + var parts = [], split = /[:.;?!] |(?: |^)["\xab]/g, index = 0; + + while (true) { + var m = split.exec(title); + var part = title.substring(index, m ? m.index : title.length); + + parts.push(unmodifiableWords.indexOf(part) !== -1 ? part : part + .replace(/\b([A-Za-z][a-z.'`()\u2019]*)\b/g, + function(all) { + return /[A-Za-z]\.[A-Za-z]/.test(all) ? all : capitalize(all); + }) + .replace(new RegExp("\\b" + small + "\\b", "ig"), + function(word) { + return word.toLowerCase(); + }) + .replace(new RegExp("^" + punct + small + "\\b", "ig"), + function(all, punct, word) { + return punct + capitalize(word); + }) + .replace(new RegExp("([-\\u2013\\u2014]\\s+)" + punct + small + "\\b", "ig"), + function(all, dash, punct, word) { + return dash + punct + capitalize(word); + }) + .replace(new RegExp("\\b" + small + punct + "$", "ig"), capitalize)); + + index = split.lastIndex; + + if (m) { + parts.push(m[0]); + } else { + break; + } + } + + return parts.join("") + .replace(/ V(s?)\. /ig, " v$1. ") + .replace(/(['`\u2019])S\b/ig, "$1s") + .replace(/\b(de|von|van|feat|n|http:\/\/)\b/ig, function(all) { + return all.toLowerCase(); + }) + .replace(/\b(AT&T|Q&A|OYF’N)\b/ig, function(all) { + return all.toUpperCase(); + }); + ; + }; + + function capitalize(word) { + return word.substr(0, 1).toUpperCase() + word.substr(1); + } + + function doWork() { + for (var ti = 0; ti < consideredTags.length; ++ti) { + var tagNr = consideredTags[ti] + var tagMask = script.toTagVersion(1 << tagNr); + if (app.selectionInfo.tag(tagNr).tagFormat) { + for (var fi = 0; fi < consideredFrames.length; ++fi) { + var name = consideredFrames[fi]; + var oldTxt = app.getFrame(tagMask, name) + var newTxt = toTitleCase(oldTxt) + if (newTxt !== oldTxt) { + app.setFrame(tagMask, name, newTxt) + } + } + } + } + if (!nextFile()) { + if (isStandalone()) { + // Save the changes if the script is started stand-alone, not from Kid3. + app.saveDirectory() + } + Qt.quit() + } else { + setTimeout(doWork, 1) + } + } + + firstFile() + doWork() + } +}
