我如何使用 Groovy 分析我的音樂目錄
最近,我一直在研究 Groovy 是如何簡(jiǎn)化略微繁瑣的 Java 的。在這篇文章中,我開始了一個(gè)簡(jiǎn)短的系列,通過創(chuàng)建一個(gè)分析我的音樂目錄的工具來演示 Groovy 腳本。
在本文中,我將演示 groovy.File? 類如何擴(kuò)展和精簡(jiǎn) java.File? 并簡(jiǎn)化其使用。這為查看音樂文件夾的內(nèi)容提供了一個(gè)框架,以確保預(yù)期的內(nèi)容(例如,cover.jpg? 文件)就位。我使用 JAudiotagger 庫(kù) 來分析音樂文件的標(biāo)簽。
安裝 Java 和 Groovy
Groovy 基于 Java,需要安裝 Java。 Java 和 Groovy 的最新和穩(wěn)定的版本可能都在你的 Linux 發(fā)行版的倉(cāng)庫(kù)中。 Groovy 也可以直接從 Apache Foundation 網(wǎng)站? 安裝。對(duì)于 Linux 用戶來說,一個(gè)不錯(cuò)的選擇是 SDKMan,它可用于獲取 Java、Groovy 和許多其他相關(guān)工具的多個(gè)版本。對(duì)于本文,我使用以下 SDK 版本:
- Java:版本 11.0.12-open 的 OpenJDK 11
- Groovy:版本 3.0.8
音樂元數(shù)據(jù)
最近,我重整了我的音樂消費(fèi)方式。我決定使用優(yōu)秀的開源 Cantata? 音樂播放器,它是開源 MPD 音樂播放器? 的一個(gè)前端。我所有的電腦的音樂都存儲(chǔ)在 /var/lib/mpd/music? 目錄下。在該音樂目錄下有藝術(shù)家子目錄,在每個(gè)藝術(shù)家子目錄下有專輯子目錄,包含音樂文件、cover.jpg,偶爾還有 PDF 格式的內(nèi)頁(yè)說明。
我絕大部分的音樂文件都是 FLAC 格式的,有一些是 MP3 格式,可能還有一小部分是 OGG 格式。我選擇 JAudiotagger 庫(kù)的一個(gè)原因是它可以透明地處理不同的標(biāo)簽格式。當(dāng)然,JAudiotagger 是開源的!
那么查看音頻標(biāo)簽有什么意義呢?以我的經(jīng)驗(yàn),音頻標(biāo)簽的管理極差。(提到音頻標(biāo)簽,)我的腦海中浮現(xiàn)出“粗心”這個(gè)詞。這是標(biāo)簽本身真正存在的問題,也可能是出于我自己的學(xué)究?jī)A向。無論如何,這是一個(gè)可以通過使用 Groovy 和 JAudiotagger 解決的重要問題。不過,它不僅適用于音樂收藏。許多其他現(xiàn)實(shí)世界的問題也適用,如需要下沉到文件系統(tǒng)中的目錄樹來處理在那里找到的內(nèi)容。
使用 Groovy 腳本
這是此任務(wù)所需的基本代碼。我在腳本中加入了注釋,這些注釋反映了我通常留給自己的(相對(duì)簡(jiǎn)寫的)“注釋提醒”:
// 定義音樂庫(kù)目錄
def musicLibraryDirName = '/var/lib/mpd/music'
// 輸出 CSV 文件標(biāo)題行
println "artistDir|albumDir|contentFile"
// 迭代音樂庫(kù)目錄中的每個(gè)目錄
// 這一層應(yīng)該是藝術(shù)家目錄
new File(musicLibraryDirName).eachDir { artistDir ->
// 迭代藝術(shù)家目錄中的每個(gè)目錄
// 這一層應(yīng)該是專輯目錄
artistDir.eachDir { albumDir ->
// 迭代專輯目錄中的每個(gè)目錄
// 這里應(yīng)該是內(nèi)容
// 或相關(guān)內(nèi)容(如 `cover.jpg`,PDF 格式的內(nèi)頁(yè)說明)
albumDir.eachFile { contentFile ->
println "$artistDir.name|$albumDir.name|$contentFile.name"
}
}
}
如上所述,我使用 groovy.File 在目錄樹中移動(dòng)。具體來說:
第 7 行創(chuàng)建一個(gè)新的 groovy.File? 對(duì)象并在其上調(diào)用 groovy.File.eachDir()?,第 7 行的 {? 和第 18 行的結(jié)尾的 }? 之間的代碼是傳給 eachDir()? 的 groovy.Colsue 參數(shù)。
這意味著 eachDir()? 為目錄中找到的每個(gè)子目錄執(zhí)行該代碼。這類似于 Java lambda(也稱為“匿名函數(shù)”)。 Groovy 閉包不會(huì)像 lambda 那樣限制對(duì)調(diào)用環(huán)境的訪問(在最新版本的 Groovy 中,如果你愿意,也可以使用 Java lambda)。如上所述,音樂庫(kù)目錄中的子目錄應(yīng)該是藝術(shù)家目錄(例如,“Iron Butterfly” 或 “Giacomo Puccini”),因此 artistDir? 是 eachDir() 傳遞給閉包的參數(shù)。
第 10 行對(duì)每個(gè) artistDir? 調(diào)用 eachDir()?,第 10 行的 {? 和第 17 行的 }? 之間的代碼形成另一個(gè)處理 albumDir 的閉包。
第 14 行,在每個(gè) albumDir? 上調(diào)用 eachFile()?,第 14 行的 {? 和第 16 行的 } 之間的代碼形成了處理專輯內(nèi)容的第三級(jí)閉包。
在本文的范圍內(nèi),我對(duì)每個(gè)文件唯一需要做的就是開始構(gòu)建信息表,我將其創(chuàng)建為一個(gè)以豎線分隔的 CSV 文件,它可以導(dǎo)入 LibreOffice? 或 OfficeOnly 或任何其他電子表格?,F(xiàn)在,代碼輸出前三列:藝術(shù)家目錄名、專輯目錄名和內(nèi)容文件名(同樣,第 2 行輸出 CSV 標(biāo)題行)。
在我的 Linux 筆記本電腦上運(yùn)行它會(huì)產(chǎn)生以下輸出:
$ 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
$
又好又快。它在一秒半內(nèi)處理近 10,000 個(gè)文件!對(duì)我來說足夠快??捎^的性能、緊湊且可讀的代碼,還有什么不喜歡的?