我如何使用 Groovy 分析我的音乐目录

为了简化 Java 的繁琐,我制作了一个 Groovy 工具来分析我的音乐目录。
2 位读者喜欢这篇文章。
Woman programming

WOCinTech Chat. 由 Opensource.com 修改. CC BY-SA 4.0

最近,我一直在研究 Groovy 如何简化 Java 的一些繁琐之处。在本文中,我将开始一个简短的系列文章,通过创建一个工具来分析我的音乐目录,以演示 Groovy 脚本。

在本文中,我将演示 groovy.File 类如何扩展和简化 java.File 并简化其使用。这为查看音乐文件夹的内容以确保预期的内容(例如,cover.jpg 文件)到位提供了一个框架。我使用 JAudiotagger 库来分析任何音乐文件的标签。

安装 Java 和 Groovy

Groovy 基于 Java,需要安装 Java。您 Linux 发行版的存储库中可能包含最新且不错的 Java 和 Groovy 版本。也可以直接从 Apache Foundation 网站安装 Groovy。对于 Linux 用户来说,一个不错的选择是 SDKMan,它可用于获取多个版本的 Java、Groovy 和许多其他相关工具。 在本文中,我使用了 SDK 发布的以下版本:

  • Java: OpenJDK 11 的 11.0.12-open 版本
  • Groovy: 3.0.8 版本

音乐元数据

最近,我整合了我的音乐消费选项。我决定使用优秀的开源 Cantata 音乐播放器,它是开源 MPD 音乐播放器守护进程的前端。我所有的电脑都将音乐存储在 /var/lib/mpd/music 目录中。在该音乐目录中是艺术家子目录,在每个艺术家子目录中是专辑子子目录,其中包含音乐文件、一个 cover.jpg 和偶尔的 liner notes 的 PDF 文件。

我的几乎所有音乐文件都是 FLAC 格式,少数是 MP3 格式,可能还有少量是 OGG 格式。我选择 JAudiotagger 库的原因之一是因为它可以透明地处理不同的标签格式。当然,JAudiotagger 是开源的!

那么,查看音频标签的意义是什么?以我的经验来看,音频标签的管理非常糟糕。“粗心”这个词浮现在脑海。但这可能更多的是我对自己吹毛求疵倾向的认可,而不是标签本身存在的真正问题。无论如何,这是一个非同小可的问题,可以通过使用 Groovy 和 JAudiotagger 来解决。它不仅适用于音乐收藏,而且许多其他实际问题包括需要下降文件系统中的目录树以对其中找到的内容进行处理。

使用 Groovy 脚本

这是此任务所需的基本代码。我已将注释合并到脚本中,这些注释反映了我通常为自己留下的(相对简短的)“注释说明”

1        // Define the music libary directory
2        def musicLibraryDirName = '/var/lib/mpd/music'
3        // Print the CSV file header
4        println "artistDir|albumDir|contentFile"
5        // Iterate over each directory in the music libary directory
6        // These are assumed to be artist directories
7        new File(musicLibraryDirName).eachDir { artistDir ->
8            // Iterate over each directory in the artist directory
9            // These are assumed to be album directories
10            artistDir.eachDir { albumDir ->
11                // Iterate over each file in the album directory
12                // These are assumed to be content or related
13                // (cover.jpg, PDFs with liner notes etc)
14                albumDir.eachFile { contentFile ->
15                    println "$artistDir.name|$albumDir.name|$contentFile.name"
16                }
17            }
18        }

如上所述,我正在使用 groovy.File 在目录树中移动。具体来说

第 7 行创建一个新的 groovy.File 对象,并对其调用 groovy.File.eachDir(),其中第 7 行上的 { 和第 18 行上的结束 } 之间的代码是 eachDir()groovy.Closure 参数。

这意味着 eachDir() 为目录中找到的每个子目录执行该代码。这类似于 Java *lambda* (也称为“匿名函数”)。 Groovy 闭包不限制对调用环境的访问,而 lambda 限制了访问 (在 Groovy 的最新版本中,如果需要,可以使用 Java lambda)。如上所述,音乐库目录中的子目录预计是艺术家目录(例如,“Iron Butterfly”或“Giacomo Puccini”),因此 artistDireachDir() 传递给闭包的参数。

第 10 行在每个 artistDir 上调用 eachDir(),其中第 10 行上的 { 和第 17 行上的 } 之间的代码形成另一个闭包,该闭包处理 albumDir

第 14 行在每个 albumDir 上调用 eachFile(),其中第 14 行上的 { 和第 16 行上的 } 之间的代码形成第三级闭包,该闭包处理专辑的内容。

就本文的范围而言,我需要对每个文件做的唯一事情是开始构建信息表,我将其创建为条形分隔的 CSV 文件,该文件可以导入到 LibreOfficeOnlyOffice 或任何其他电子表格中。现在,代码写出前三列:艺术家目录名称、专辑目录名称和内容文件名(此外,第 2 行写出 CSV 标题行)。

在我的 Linux 笔记本电脑上运行此程序会产生以下输出

$ groovy TagAnalyzer.groovy | head
artistDir|albumDir|contentFile
Habib Koite & Bamada|Afriki|02 - Ntesse.flac
Habib Koite & Bamada|Afriki|08 - NTeri.flac
Habib Koite & Bamada|Afriki|01 - Namania.flac
Habib Koite & Bamada|Afriki|07 - Barra.flac
Habib Koite & Bamada|Afriki|playlist.m3u
Habib Koite & Bamada|Afriki|04 - Fimani.flac
Habib Koite & Bamada|Afriki|10 - Massake.flac
Habib Koite & Bamada|Afriki|11 - Titati.flac
Habib Koite & Bamada|Afriki|03 – Africa.flac
[...]
Richard Crandell|Spring Steel|04-Japanese Lullaby [Richard Crandell].flac
Richard Crandell|Spring Steel|Spring Steel.pdf
Richard Crandell|Spring Steel|03-Zen Dagger [Richard Crandell].flac
Richard Crandell|Spring Steel|cover.jpg
$

在性能方面

$ time groovy TagAnalyzer.groovy | wc -l
9870

real        0m1.482s
user        0m4.392s
sys        0m0.230s
$

非常棒且快速。 它在一秒半内处理了近 10,000 个文件! 对我来说足够快了。 可观的性能、紧凑且可读的代码——有什么不喜欢的呢?

在我的下一篇文章中,我将打开 JAudiotagger 接口并查看每个文件中的标签。

Chris Hermansen portrait Temuco Chile
自从 1978 年从不列颠哥伦比亚大学毕业以来,我就离不开某种类型的计算机。自 2005 年以来,我一直是一名全职 Linux 用户,从 1986 年到 2005 年是一名全职 Solaris 和 SunOS 用户,在那之前是一名 UNIX System V 用户。

评论已关闭。

Creative Commons License本作品根据 Creative Commons Attribution-Share Alike 4.0 International License 许可。
© . All rights reserved.