Python3.根據ID3v2批量修改mp3文件名

我聽歌並不專業,沒有特定的口味,沒有特定的播放軟件,於是,隨着換手機、換電腦、重裝系統、朋友分享等等,我有了一堆mp3文件,而且越聚越多。

由於這些文件來源就亂七八糟的,文件名的格式有的是“歌曲名-歌手”,有的是“歌手-歌曲名”,甚至有些亂碼的文件名。曾經幾度想要整理一下,於是這些mp3文件被分爲了“未整理”、“已整理”的文件夾,“未整理”文件夾下還有“手機1”、“手機2”等等文件夾,幾次下來都沒成功整理完。

網上一定有用來整理音樂收藏的軟件,但懶得找了,我來寫代碼幹它。

ID3v2

作爲聽歌聽個響的非專業用戶,之前播放這些音樂文件時發現一些現象:

  • 有的歌播放的時候會顯示一張封面,甚至有歌詞
  • 有的歌文件名被改錯,但播放時依然能正確顯示歌名
  • 查看大多數mp3文件的文件屬性時有比較詳細的“標題”、“參與創作的藝術家”等等信息

所以可以判斷,mp3文件本身就有歌名、歌手的信息,不受文件名的影響。那麼如果用程序直接讀取mp3文件裏面的歌名、歌手的信息,就可以實現批量重命名了。

大概查了一下,應該就是ID3v2這個東西了,根據定義,它位於音樂文件的文件頭部,分爲標籤頭和標籤幀。

標籤頭爲文件頭10個字節,標記了是否使用了ID3v2標籤,以及標籤幀總共有多大,讀取函數如下,輸入爲頭10個字節,輸出爲標籤幀總字節數,假如不是ID3v2標籤則輸出None:

def parse_ID3V2_head(head_bin):
    if head_bin[:3] != b'ID3':
        return None
    frames_bin_size = (head_bin[6] << 21 | head_bin[7] << 14 |
                       head_bin[8] << 7 | head_bin[9])
    return frames_bin_size

需要說明的是,描述整個ID3v2標籤字節數的那4個字節最高位不使用,即這4個字節剩下的7位拼到一起所表示的二進制數纔是字節數,真是詭異。

標籤幀裏面就是各種信息,讀取函數如下,輸入爲標籤幀的字節流,輸出標籤字典,其中TIT2表示標題,TPE1表示歌手,已經decode爲字符串:

import struct
encondings = ['GBK', 'UTF-16', 'UTF-16BE', 'UTF-8']
def parse_ID3V2_frames(frames_bin):
    pointer = 0
    frames_bin_size = len(frames_bin)
    frames = {}
    while pointer < frames_bin_size - 10:
        frame_header_bin = frames_bin[pointer : pointer+10]
        # frame_header = (ID, Size, Flags)
        frame_header = struct.unpack('>4sI2s', frame_header_bin)
        frame_body_size = frame_header[1]
        if frame_body_size == 0:
            break
        pointer += 10
        frames[frame_header[0]] = frames_bin[pointer : pointer+frame_body_size]
        pointer += frame_body_size
    TIT2_bin = frames.get(b'TIT2', None)
    TPE1_bin = frames.get(b'TPE1', None)
    if TIT2_bin:
        enconding = encondings[TIT2_bin[0]]
        frames[b'TIT2'] = TIT2_bin[1:].decode(enconding)
    if TPE1_bin:
        enconding = encondings[TPE1_bin[0]]
        frames[b'TPE1'] = TPE1_bin[1:].decode(enconding)
    return frames

需要說明的是,很多中文歌曲的信息編碼爲GBK,但標記爲ISO-8859-1,好在ISO-8859-1似乎是GBK的子集,於是所有ISO-8859-1編碼的字節我都用GBK來解碼了,我可真是個小機靈鬼。

批量修改文件名

那麼通過如下腳本,就可以實現將一個文件夾下所有使用ID3v2標籤的音樂文件按想要的格式重命名了,我想統一整理爲“歌曲名-歌手”的格式。

src_dir = r'E:\Music\test'
dst_dir = r'E:\Music\test_result'

file_names = os.listdir(src_dir)

for file_name in file_names:
    file_old_path = rf'{src_dir}\{file_name}'
    f = open(file_old_path, 'rb')
    frames_bin_size = parse_ID3V2_head(f.read(10))
    if frames_bin_size is None:
        f.close()
        continue
    frames_bin = f.read(frames_bin_size - 10)
    f.close()
    frames = parse_ID3V2_frames(frames_bin)
    TIT2 = frames[b'TIT2']
    TPE1 = frames[b'TPE1']
    file_new_path = rf'{dst_dir}\{TIT2}_{TPE1}.{file_name.split(".")[-1]}'
    if os.path.exists(file_new_path):
        continue
    else:
        os.rename(file_old_path, file_new_path)

在整理前是這樣的

在這裏插入圖片描述

整理後是這樣的

在這裏插入圖片描述

可以看到,所有文件名都按照“歌曲名-歌手”的格式重命名了,而且不限於mp3文件,有些其他類型文件如flac只要使用了ID3v2標籤就可以一起整理。

結論與展望

基本實現了讀取ID3v2標籤,並可以用於實現文件名的重命名。

ID3v2標籤中其他信息如封面、歌詞等處理方式有待進一步研究。部分音樂文件並沒有使用ID3v2標籤而是其它標籤,不在本文討論範圍內,有待進一步研究。可能需要製作可視化界面進一步管理音樂文件。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章