在本系列文章中,我将开发几个脚本来帮助清理我的音乐收藏。在上一篇文章中,我编写并测试了一个 Groovy 脚本来清理标签字段的混合集合。在本文中,我将把我一直在使用的框架分离到一个单独的类中,然后编写一个测试程序来运行它。
安装 Java 和 Groovy
Groovy 基于 Java,需要安装 Java。您的 Linux 发行版的存储库中可能包含最新且不错的 Java 和 Groovy 版本。也可以按照 Groovy 首页上的说明安装 Groovy。对于 Linux 用户来说,SDKMan 是一个不错的替代方案,它可以用来获取多个版本的 Java、Groovy 和许多其他相关工具。在本文中,我使用的是 SDK 发布的版本:
- Java:OpenJDK 11 的 11.0.12-open 版本;
- Groovy:3.0.8 版本。
回到问题
如果您还没有阅读本系列文章的第 1-5 部分,请先阅读,以便您了解我的音乐目录的预期结构、该文章中创建的框架以及我们如何获取 FLAC、MP3 和 OGG 文件。
- 我如何使用 Groovy 分析我的音乐目录
- 我最喜欢的用于分析音乐文件的开源库
- 我如何使用 Groovy 分析我的音乐目录中的专辑封面
- 使用 Groovy 清理音乐目录中不需要的文件
- 使用 Groovy 脚本清理音乐标签
框架类
正如我已经多次提到的,由于音乐目录结构,我们有一个标准的框架来读取艺术家子目录、专辑子子目录、音乐以及其中包含的其他文件。与其将该代码复制到每个脚本中,不如创建一个 Groovy 类来封装通用框架行为,并将特定于应用程序的行为委托给调用它的脚本。
这是框架,已移至 Groovy 类中
1 public class TagAnalyzerFramework {
2 // called before any data is processed
3 Closure atBeginning
4 // called for each file to be processed
5 Closure onEachLine
6 // called after all data is processed
7 Closure atEnd
8 // the full path name to the music library
9 String musicLibraryDirName
10 public void processMusicLibrary() {
11 // Before we start processing...
12 atBeginning()
13 // Iterate over each dir in music library
14 // These are assumed to be artist directories
15 new File(musicLibraryDirName).eachDir { artistDir ->
16 // Iterate over each dir in artist dir
17 // These are assumed to be album directories
18 artistDir.eachDir { albumDir ->
19 // Iterate over each file in the album directory
20 // These are assumed to be content or related
21 // (cover.jpg, PDFs with liner notes etc)
22 albumDir.eachFile { contentFile ->
23 // Then on each line...
24 onEachLine(artistDir, albumDir, contentFile)
25 }
26 }
27 }
28 // And before we finish...
29 atEnd()
30 }
31 }
第 1 行介绍了公共类名。
第 2-7 行声明了应用程序脚本用于定义所需处理细节的三个闭包。这被称为行为委托。
第 8-9 行声明了保存音乐目录文件名的字符串。
第 10-30 行声明了实际处理的方法。
第 12 行调用在处理任何数据之前运行的 Closure
。
第 15-27 行循环遍历艺术家/专辑/内容文件结构。
第 24 行调用处理每个内容文件的 Closure
。
第 29 行调用在处理完所有数据后运行的 Closure
。
我想在使用这个类之前编译它,如下所示
$ groovyc TagAnalyzerFramework.groovy$
这就是框架的全部内容。
在脚本中使用框架
这是一个简单的脚本,它打印出音乐目录中所有文件的竖线分隔值列表
1 int fileCount
2 def myTagAnalyzer = new TagAnalyzerFramework()
3 myTagAnalyzer.atBeginning = {
4 // Print the CSV file header and initialize the file counter
5 println "artistDir|albumDir|contentFile"
6 fileCount = 0
7 }
8 myTagAnalyzer.onEachLine = { artistDir, albumDir, contentFile ->
9 // Print the line for this file
10 println "$artistDir.name|$albumDir.name|$contentFile.name"
11 fileCount++
12 }
13 myTagAnalyzer.atEnd = {
14 // Print the file counter value
15 System.err.println "fileCount $fileCount"
16 }
17 myTagAnalyzer.musicLibraryDirName = '/home/clh/Test/Music'
18 myTagAnalyzer.processMusicLibrary()
第 1 行定义了一个局部变量 fileCount
,用于计算内容文件的数量。请注意,此变量不需要是 final。
第 2 行调用 TagAnalyzerFramework
类的构造函数。
第 3 行执行了在 Java 中看起来像错误的操作。它似乎引用了外部类中的字段。但是,在 Groovy 中,这实际上是在调用该字段上的 setter,因此这是可以接受的,只要实现类“记住”它有一个提供此属性的 setter 的约定即可。
第 3-7 行创建了一个 Closure
,用于打印竖线分隔值标题并初始化 fileCount
变量。
第 8-12 行类似地定义了 Closure
,用于处理每行的逻辑。在这种情况下,它只是打印艺术家、专辑和内容文件名。如果我回顾 TagAnalyzerFramework
的第 24 行,我看到它使用与此处显示的参数相对应的三个参数调用此 Closure
。
第 13-16 行定义了在读取所有数据后包装处理的 Closure。在这种情况下,它将文件计数打印到标准错误。
第 17 行设置音乐库目录名称。
第 18 行调用该方法来处理音乐库。
运行脚本
$ groovy MyTagAnalyzer.groovy
artistDir|albumDir|contentFile
Bombino|Azel|07_Igmayagh_Dum_1.6.16.mp3
Bombino|Azel|08_Ashuhada_1.6.16.mp3
Bombino|Azel|04_Tamiditine_Tarhanam_1.6.16.mp3
Bombino|Azel|10_Naqqim_Dagh_Timshar_1.6.16.mp3
[...]
St Germain|Tourist|04_-_St Germain_-_Land Of....flac
fileCount 55
$
当然,编译框架类创建的 .class
文件必须在类路径上才能使其工作。当然,我可以使用 jar 来打包这些类文件。
那些因在外部类中设置字段而感到不安的人可以定义闭包的本地实例,并将这些实例作为参数传递给构造函数或 processMusicLibrary()
,并达到相同的效果。
我可以回到早期文章中提供的代码示例,以改造此框架类。我将把这个练习留给读者。
行为委托
对我来说,这里最酷的事情是行为委托,这在其他语言中需要各种技巧。多年来,Java 需要匿名类和相当多的额外代码。Lambda 在很大程度上解决了这个问题,但它们仍然不能引用其范围之外的非 final 变量。
这就是本系列关于使用 Groovy 管理我的音乐库中的标签的全部内容。未来还会有更多关于 Groovy 的文章。
评论已关闭。