为了简化Java的笨拙,我做了一个Groovy工具来分析我的音乐目录。
最近,我一直在研究Groovy是如何精简Java的轻微笨拙的。在这篇文章中,我开始了一个简短的系列,通过创建一个分析我的音乐目录的工具来演示Groovy脚本。
在这篇文章中,我演示了groovy.File 类如何扩展和精简java.File ,并简化其使用。这提供了一个框架,用于查看音乐文件夹的内容,以确保预期的内容(例如,cover.jpg 文件)已经到位。我使用JAudiotagger库来分析任何音乐文件的标签。
安装Java和Groovy
Groovy是基于Java的,需要安装Java。最近的、像样的Java和Groovy版本都可能在你的Linux发行版的软件库中。Groovy也可以直接从Apache基金会的网站上安装。对于Linux用户来说,一个不错的选择是SDKMan,它可以用来获取多个版本的Java、Groovy和许多其他相关工具。在这篇文章中,我使用了SDK的以下版本。
- Java:OpenJDK 11的11.0.12-open版本
- Groovy:版本3.0.8
音乐元数据
最近,我整合了我的音乐消费选择。我决定使用优秀的开源Cantata音乐播放器,它是开源MPD音乐播放器守护程序的一个前端。我所有的电脑的音乐都存储在/var/lib/mpd/music 目录中。在该音乐目录下有艺术家子目录,在每个艺术家子目录下有包含音乐文件的专辑子目录,cover.jpg ,有时还有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行的关闭的} 之间的代码是groovy.Closure 的参数,用于eachDir() 。
这意味着,eachDir() 对目录中发现的每个子目录都执行该代码。这类似于Java的lambda(也叫 "匿名函数")。Groovy闭包不会像lambda那样限制对调用环境的访问(在最近的Groovy版本中,如果你想的话,可以使用Java lambdas)。如上所述,音乐库目录内的子目录被期望为艺术家目录(例如,"铁蝴蝶 "或 "贾科莫-普契尼"),所以artistDir 是由eachDir() 传递给closure的参数。
第10行在每个artistDir ,调用eachDir() ,第10行的{ 和第17行的} 之间的代码形成另一个闭包,处理albumDir 。
第14行,在每个albumDir ,调用eachFile() ,第14行的{ 和第16行的} 之间的代码形成第三级闭包,处理相册的内容。
在本文的范围内,我需要对每个文件做的唯一事情是开始建立信息表,我将其创建为一个以条形分隔的CSV文件,可以导入LibreOffice或OnlyOffice,或任何其他电子表格。现在,代码写出了前三列:艺术家目录名、专辑目录名和内容文件名(另外,第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个文件!对我来说已经足够快了。对我来说已经足够快了。令人尊敬的性能,紧凑和可读的代码--有什么不喜欢的呢?