replica

  初衷是想要整理iphone中的音乐。IOS(我自己的手机还是IOS8.3,新版本的系统可能有变化了)自带的音乐软件中所有音乐文件都存放在/var/mobile/Media/iTunes_Control里面。不过很令人抓狂的是首先这个目录被分隔成了从F00-Fxx的多个子目录,我的手机上总共到F49,mp3文件都放在这些子目录中。其次,mp3文件名全部都被点窜了,是看起来毫无规律的随机四位大写字母。每隔一段时间我都想从手机中把音乐备份出来然后放到电脑上,但是不知道文件名的话维护起来很麻烦。所以需要一个小程序来根据mp3的信息改变文件名。

  程序的目标非常简单,就是首先提取MP3文件中的信息,主要提取其歌名和演唱者两个字段,然后把这个文件重命名成 “歌名 - 演唱者.mp3”这种格式。

  

■  关于mp3信息的提取方法

  mp3除了音频信息的部分外,在文件的某些地方还会存放有关于这个音频的一些基本信息比如作者,创作时间,专辑名,曲序号,专辑图片等等。这些信息被统称为ID3标签。ID3标签被分成两代,第一代ID3v1只存储一些很简单的信息,占用文件末尾的128个字节(下面的直接读文件的就是默认是ID3v1的情况)。而ID3v2位于文件的开头,并且包含很全的信息比如加上一张专辑插图。

  网上搜到最多的使用ID3或者类似的一些模块进行提取,也有很多人根据mp3的文件结构特征直接对文件进行读取操作然后过滤出信息。因为我手上的资源很杂而且试了一下第二种方法发现很多文件提取信息都不全或者错误,所以还是用了第一种方法。下面这个是从网上摘来的,某种比较简洁的第二种方法示例:这个函数读取一个文件,然后从文件内容的特定位置解析出tag信息并返回一个字典。

def getID3(filename):
fp = open(filename, 'r')
fp.seek(-128, 2) fp.read(3) # TAG iniziale
title = fp.read(30)
artist = fp.read(30)
album = fp.read(30)
anno = fp.read(4)
comment = fp.read(28) fp.close() return {'title':title, 'artist':artist, 'album':album, 'anno':anno}

  和网上很多人用ID3或者eyed3之类的模块不同,我用了个相对比较冷门的replica,他一共就只有三个模块文件cli.py , tagger.py , cloner.py。我们用到的主要是tagger。基本用法如下:

from replica import tagger

tags = tagger.get_tags("PATH.mp3")  #获取一个mp3文件的ID3标签信息

tagger.set_tags(tags,"ANOTHER.mp3")

  get_tags方法得到的是一个mutagen的MP3对象(replica是基于mutagen的,mutagen是更基本一些的音频处理模块)。这个对象所属的类应该实现了__getattr__方法,所以你可以像一个字典一样去访问这个对象中的一些键值。而如果打印这个这个对象看到的就是一个字典:

for k,v in tags.items():
if k == u"APIC:": #跳过U'APIC:'这个键是因为这个键的值是专辑图片,如果用字符来表示的话太大了这里显示不下
continue
print k,v
print repr(k),repr(v)
###打印结果###
TDRC 2011
TIT2 ゆりゆららららゆるゆり大事件
ゆりゆららららゆるゆり 大事件
TRCK 1/4
TPE1 七森中☆ごらく部
TALB ゆりゆららららゆるゆり大事件
TSRC JPPC01101395
TCON Anime
TXXX:DISCID 28036204 ###repr结果###
'TDRC' TDRC(encoding=<Encoding.LATIN1: 0>, text=[u'2011'])
'TIT2' TIT2(encoding=<Encoding.UTF16: 1>, text=[u'\u3086\u308a\u3086\u3089\u3089\u3089\u3089\u3086\u308b\u3086\u308a\u5927\u4e8b\u4ef6'])
u'USLT::eng' USLT(encoding=<Encoding.UTF16: 1>, lang='eng', desc=u'', text=u'\u3086\u308a\u3086\u3089\u3089\u3089\u3089\u3086\u308b\u3086\u308a\r\u3086\u308a\u3086\u3089\u3089\u3089\u3089\u3086\u308b\u3086\u308a\r\u3086\u308a\u3086\u3089\u3089\u3089\u3089\u3086\u308b\u3086\u308a \u5927\u4e8b\u4ef6\r\u3066\u3093\u3066\u3053\u307e\u3044\u306e\u4eca\u65e5\u660e\u65e5\u5927\u7206\u767a\r\u305d\u3093\u3067\u8eab\u9577\u4f38\u3073\u306a\u3044\u3084\u3042\u3042\u3069\u3046\u3057\u3088\r\u7518\u3044\u3082\u306e\u98df\u3079\u3059\u304d\u30c6\u30fc\u30de\u30d0\u30fc\u30af\r\u307b\u3044\u3058\u3083\u96a0\u305b\u3088\u4e59\u5973\u3067\u3069\u3046\u3058\u3083\u308d\r\u90e8\u6d3b\u52d5\u672c\u756a\r\u3057\u3081\u3057\u3081\u7121\u9045\u523b\r\u672c\u696d\u5b78\u696d\u306a\u306b\u305d\u308c\r\u305d\u3093\u306a\u306e\u305c\u3093\u305c\u3093\u305c\u3093\u305c\u3093\u305c\u3093\u305c\u3093\u98df\u3079\u308c\u306a\u3044\r\u685c\u54b2\u304d \uff08\u685c\u54b2\u304d\uff09\r\u685c\u6563\u308a \uff08\u685c\u6563\u308a\uff09\r\u660e\u65e5\u3082\u3044\u3044\u65e5\u3068\u6b4c\u3046\u3088\r\u541b\u304c\u597d\u304d \uff08\u541b\u304c\u597d\u304d\uff09\r\u541b\u304c\u3044\u3044 \uff08\u541b\u304c\u3044\u3044\uff09\r\u660e\u65e5\u3082\u3044\u305f\u3044\u3068\u601d\u3046\u3088\r\u6700\u7d42\u624b\u6bb5\u3067\u5c40\u7720\u308a\r\u3086\u308a\u3086\u3089\u3089\u3089\u3089\u3086\u308b\u3086\u308a\r\u3086\u308a\u3086\u3089\u3089\u3089\u3089\u3086\u308b\u3086\u308a\r\u3086\u308a\u3086\u3089\u3089\u3089\u3089\u3086\u308b\u3086\u308a \u5927\u4e8b\u4ef6(\u3060\u3044\u3058\u3051\u3093)\r\u3044\u308d\u306f\u306b\u307b\u3078\u3068\u3067\u304a\u307f\u304f\u3058Get you\uff01\u3042\u308a\u3089\u3073\u3085\u30fc\r\u306b\u3083\u3093\uff01\u306b\u3083\u3093\uff01\u8001\u306b\u3083\u3093\uff01\u82e5\u306b\u3083\u3093\uff01\u7537\u306b\u3083\uff01\u5973\u3093\uff01\r\u5fc5\u6b7b\u306b\u5bc6\u66f8\u3092\u767a\u5c04\u3067\u5fc5\u4e2d\u6388\u696d\u4e2d\r\u3067\u3082\u306d\u3042\u3089\u3089\u3089\u30c1\u30e7\u30fc\u30af\u304c\u30df\u30b5\u30a4\u30eb\r\u864e\u7a74\u306b\u5165\u3089\u306a\u3044\r\u96e8\u306a\u3089\u30cf\u30ec\u30eb\u30e4\r\u30ab\u30e9\u30aa\u30b1\u5272\u9ad8\u306a\u306b\u305d\u308c\r\u305d\u3093\u306a\u306e\u305c\u3063\u305f\u3044\u305c\u3063\u305f\u3044\u305c\u3063\u305f\u3044\u98df()\u3079\u308c\u306a\u3044\uff01\uff01\r\u685c\u54b2\u304d \uff08\u685c\u54b2\u304d\uff09\r\u685c\u6563\u308a \uff08\u685c\u6563\u308a\uff09\r\u660e\u65e5\u3082\u3044\u3044\u65e5\u3068\u6b4c\u3046\u3088\r\u541b\u304c\u597d\u304d \uff08\u541b\u304c\u597d\u304d\uff09\r\u541b\u304c\u3044\u3044 \uff08\u541b\u304c\u3044\u3044\uff09\r\u660e\u65e5\u3082\u3044\u305f\u3044\u3068\u601d\u3046\u3088\r\u8fd1\u6240\u306e\u30ef\u30f3\u30b3\u3068\u683c\u95d8\r\u3086\u308a\u3086\u3089\u3089\u3089\u3089\u3086\u308b\u3086\u308a\r\u3086\u308a\u3086\u3089\u3089\u3089\u3089\u3086\u308b\u3086\u308a\r\u3086\u308a\u3086\u3089\u3089\u3089\u3089\u3086\u308b\u3086\u308a \u5927\u4e8b\u4ef6\r\u30c1\u30e3\u30a4\u30e0\u304c\u76ee\u899a\u307e\u3057\rKIRA\u2606KIRA\u2606TSUN-DELE\r\u751f\u30af\u30ea\u30fc\u30e0\u60d1\u661f \u306a\u306b\u305d\u308c\r\u305d\u308c\u306a\u3089\u307b\u3093\u3068\u306b\u307b\u3093\u3068\u306b\u307b\u3093\u3068\u306b\u3084\u3081\u308c\u306a\u3044\uff01\u3084\u3081\u308c\u306a\u3044\uff01\uff01\r\u685c\u54b2\u304d \uff08\u685c\u54b2\u304d\uff09\r\u685c\u6563\u308a \uff08\u685c\u6563\u308a\uff09\r\u660e\u65e5\u3082\u3044\u3044\u65e5\u3068\u6b4c\u3046\u3088\r\u541b\u304c\u597d\u304d \uff08\u541b\u304c\u597d\u304d\uff09\r\u541b\u304c\u3044\u3044 \uff08\u541b\u304c\u3044\u3044\uff09\r\u660e\u65e5\u3082\u3044\u305f\u3044\u3068\u601d\u3046\u3088\r\u6700\u7d42\u624b\u6bb5(\u3067\u5c40\u7720\u308a\r\u8fd1\u6240\u306e\u30ef\u30f3\u30b3\u3068\u683c\u95d8\r\u9769\u547d\u8d77\u3053\u3057\u3066\u5352\u696d\r\u3086\u308a\u3086\u3089\u3089\u3089\u3089\u3086\u308b\u3086\u308a\r\u3086\u308a\u3086\u3089\u3089\u3089\u3089\u3086\u308b\u3086\u308a\r\u3086\u308a\u3086\u3089\u3089\u3089\u3089\u3086\u308b\u3086\u308a \u5927\u4e8b\u4ef6\r')
'TRCK' TRCK(encoding=<Encoding.LATIN1: 0>, text=[u'1/4'])
'TPE1' TPE1(encoding=<Encoding.UTF16: 1>, text=[u'\u4e03\u68ee\u4e2d\u2606\u3054\u3089\u304f\u90e8'])
'TALB' TALB(encoding=<Encoding.UTF16: 1>, text=[u'\u3086\u308a\u3086\u3089\u3089\u3089\u3089\u3086\u308b\u3086\u308a\u5927\u4e8b\u4ef6'])
'TSRC' TSRC(encoding=<Encoding.UTF16: 1>, text=[u'JPPC01101395'])
'TCON' TCON(encoding=<Encoding.UTF16: 1>, text=[u'Anime'])
u'TXXX:DISCID' TXXX(encoding=<Encoding.UTF16: 1>, desc=u'DISCID', text=[u'28036204'])

  字典本身打印出来是这样:

{u'APIC:':'关于图片信息的内容','TSRC': TSRC(encoding=<Encoding.UTF16: 1>, text=[u'JPPC01101395']), 'TCON': TCON(encoding=<Encoding.UTF16: 1>, text=[u'Anime']), u'TXXX:DISCID': TXXX(encoding=<Encoding.UTF16: 1>, desc=u'DISCID', text=[u'']),xxxx还有一些,意思一下。。}

  结合各个键的值大概就可以猜出来这个键是什么意思了。对于没有设置某个标签信息的文件而言,它的tags对象中就不会有相关的键。值其实是一些对象,比如标题标签的键是TIT2,值就是一个TIT2对象,其有两个关键的属性,分别是TIT2.encoding和TIT2.text分别指出了显示标题时用的编码格式和标题文本组成的单个元素的列表。

  这些对象的全图鉴可以参见mutagen/id3/_frames.py的源码。只是关于如何改变或创建这些对象,改变一个既有的MP3的标签信息这一方面还有待研究(其实是mutagen的内容了)

  其实就论replica这个模块的用法的话就是以上了,然而我在之后的编写过程中又遇到了各种各样的坑,比如编码问题,windows系统对于文件名的要求等等,所以打算继续写下去。

■  关于文本信息的编码

  在tag信息的对象中,大多承载文字信息的对象都有encoding和text两个属性,且text属性中是一个单unicode元素的列表。其实这两个属性联合起来就表示了这个unicode应该用哪种编码进行encode才能成为原本的信息。

  首先来看encoding这属性。这个属性其实是维护了一个mutagen中的Encoding对象,这个对象可以在源码中看到(位于mutagen/id3/_specs.py),把四种编码分别用了一个数字表示,在知道了对应关系之后我们可以在自己的脚本里添加一个同样的字典使得使用更加方便:

ENCODING = {0: "latin1", 1: "utf16", 2: "utf16be", 3: "utf8"}

  因为从相关对象中取到的属性本质就是一个数,比如TIT2.Encoding其实就是1.

  在四种编码格式中,比较特殊的是latin1这种格式。latin1就是ISO-8859-1,之前在en/decode以及print探索那篇文章中也提到过,被latin1 decode出来的unicode只能被latin1 encode成str,并且encode得到的东西的编码格式和最原先的是一样的。

■  windows中文件名的规则

  windows中的文件名不能含有字符 \/:*?"<>| 中的任意一个。如果含有这些错误的话在运用os.rename或者其他类似的重命名手段的时候会报错WindowsError123。另一方面,如果同目录下已经有相同名字文件存在时重命名会报错WindowsError183。

  一般而言,对windows上的文件进行命名的时候我们可以直接用unicode类型的字符串(如果愿意,也可以用gb这个系列的编码格式的字符串进行命名,不过能用unicode的情况下应该尽量用通用性更加好的unicode)。但是就这个程序而言,我们碰到了很多由latin1编码格式得到的unicode,很遗憾在处理如果用这些unicode直接去命名文件,会出现乱码。目前我能想到的解决办法是在进行命名之前进行一个判断,如果这个文本信息的编码格式是latin1的,那么在将其输出成文件名的一部分之前先把它encode("latin1"),如果不是,那么可以直接把它作为文件名的一部分去rename。

  

■  关于其他一些小改善

  至此整个小程序基本功能已经实现了,接下来就是一些业务逻辑的改善了。比如tag信息不全时怎么办,重命名失败时怎么办,增加处理进度提示,对于目录中非mp3文件的处理等等。最后整个脚本如下:

#!/usr/bin/env python
# coding=utf8
import os
import sys
import logging
import re
from replica import tagger reload(sys)
sys.setdefaultencoding("gb18030") ENCODING = {0: "latin1", 1: "utf16", 2: "utf16be", 3: "utf8"} logging.basicConfig(filename="mp3.log", level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s ") def modify_textual(string):
return re.sub(u'[\\\/:\*\?"<>\|]', " ", string) def process_one_song(songName):
mp3 = tagger.get_tags(u"{songname}".format(songname=songName))
try:
artist = mp3.get("TPE1").text[0]
enco_form = ENCODING.get(mp3.get("TPE1").encoding)
if enco_form == "latin1":
artist = artist.encode("latin1")
except AttributeError as e:
artist = u"未知艺术家"
try:
name = mp3.get("TIT2").text[0]
enco_form = ENCODING.get(mp3.get("TIT2").encoding)
if enco_form == "latin1":
name = name.encode("latin1")
except AttributeError as e:
name = u"未知曲名"
name = modify_textual(name)
artist = modify_textual(artist)
logging.info(u"{songname}".format(songname=songName) +
u"{filename}.mp3".format(
filename=os.path.dirname(songName) + os.sep.decode("utf8") + name + u"-" + artist))
try:
os.rename(u"{songname}".format(songname=songName), u"{filename}.mp3".format(
filename=os.path.dirname(songName) + os.sep.decode("utf8") + name + u"-" + artist))
except WindowsError as windowserror:
logging.error(
"error processing {name} for windows error [{error}]".format(name=songName, error=str(windowserror))) def main():
try:
rootpath = sys.argv[1]
except IndexError as e:
rootpath = 'mp3'
if not os.path.isdir(rootpath):
print u"输入目录不存在"
sys.exit(1)
count = 0
total = 0
for root, dir, files in os.walk(rootpath):
total += len(files)
for root, dir, files in os.walk(rootpath):
for file in files:
filename = os.path.join(root.decode("gb18030"), file.decode("gb18030"))
if os.path.splitext(file)[1] != '.mp3':
os.remove(filename)
continue
count += 1
print u"正在处理{percent:.0f}%的歌".format(percent=float(count) / total * 100)
process_one_song(filename)

【Python】 获取MP3信息replica的更多相关文章

  1. python 获取对象信息

    当我们拿到一个对象的引用时,如何知道这个对象是什么类型.有哪些方法呢? 使用type() 首先,我们来判断对象类型,使用type()函数: 基本类型都可以用type()判断: >>> ...

  2. Python 获取车票信息

    提示:该代码仅供学习使用,切勿滥用!!! 先来一个git地址:https://gitee.com/wang_li/li_wang 效果图: 逻辑: 1.获取Json文件的内容 2.根据信息生成URL ...

  3. 用python获取服务器硬件信息[转]

    #!/usr/bin/env python # -*- coding: utf-8 -*- import rlcompleter, readline readline.parse_and_bind(' ...

  4. 用python获取ip信息

    1.138网站 http://user.ip138.com/ip/首次注册后赠送1000次请求,API接口请求格式如下,必须要有token值 import httplib2 from urllib.p ...

  5. python获取对象信息

    获取对象信息 拿到一个变量,除了用 isinstance() 判断它是否是某种类型的实例外,还有没有别的方法获取到更多的信息呢? 例如,已有定义: class Person(object): def ...

  6. python获取机器信息脚本(网上寻找的)

    获取机器信息(待测试) # -*- coding: UTF-8 -*- import psutil import json import os import socket import struct ...

  7. python获取的信息列表微信公共平台和用户头像

    转载注明原文地址:http://blog.csdn.net/btyh17mxy/article/details/25207889 只写模拟登陆的方式获取微信从信息和头像库列表公共平台, - 相关后,功 ...

  8. 关于Python 获取windows信息收集

    收集一些Python操作windows的代码 (不管是自带的or第三方库)均来自网上 1.shutdown 操作 定时关机.重启.注销 #!/usr/bin/python #-*-coding:utf ...

  9. Python - 获取帮助信息

    1- Python Manuals 自带CHM格式的Python Manuals存放在\Python<x.x>\Doc\目录下.可以在IDLE界面下按F1键或点击help选项下Python ...

随机推荐

  1. 10种软件开发中 over-engineering 的错误套路

    别把「不要过度使用 Generic」误解成「不用 Generic」,也别把「不要写一些不必要的 Wrapper」误解成「不写任何 Wrapper」.我只是在讲 over-engineering 这个事 ...

  2. Android查缺补漏(线程篇)-- AsyncTask的使用及原理详细分析

    本文作者:CodingBlock 文章链接:http://www.cnblogs.com/codingblock/p/8515304.html 一.AsyncTask的使用 AsyncTask是一种轻 ...

  3. Java语法 示例

    第二章: int:整型 double:双精度浮点型 char:字符型 String:字符串型语法:数据类型 变量名: 如:String name: 变量名=值: 如:name="张三&quo ...

  4. html中的div span和frameset框架标签

    Div和span 1.      div独占一层,由div九不允许有别的. 2.      span标签不是独自占用一行,span一般用来设置字体. 框架标签: 什么是框架标签,就是把一个页面分成很多 ...

  5. Python机器学习中文版

    Python机器学习简介 第一章 让计算机从数据中学习 将数据转化为知识 三类机器学习算法 第二章 训练机器学习分类算法 透过人工神经元一窥早期机器学习历史 使用Python实现感知机算法 基于Iri ...

  6. WebService就是这么简单

    WebService介绍 首先我们来谈一下为什么需要学习webService这样的一个技术吧.... 问题一 如果我们的网站需要提供一个天气预报这样一个需求的话,那我们该怎么做????? 天气预报这么 ...

  7. assert断言检测

    assert 是宏,非函数,包含在assert.h 头文件中. 如果其后面括号里的值为假,则程序终止运行,并提示出错.这个 宏只在 Debug 版本上起作用,而在 Release 版本被编译器完全优化 ...

  8. Vim修炼秘籍之语法篇

    前言 少年,我看你骨骼精奇,是万中无一的武学奇才,维护世界和平就靠你了,我这有本秘籍<Vim修炼秘籍>,见与你有缘,就十块卖给你了! 如果你是一名 Vimer,那么恭喜你,你的 Vim 技 ...

  9. 性能优化之reflow和repaint

    本文主要介绍一下什么是reflow,repaint, 怎样避免它们造成的不良影响, 怎么通过工具查看分析它们. 一.首先对浏览器渲染引擎下网页呈现过程简要说一下: 浏览器的渲染引擎开始解析html构建 ...

  10. 【Unity3D与23种设计模式】组合模式(Composite)

    前段时间在忙一个新项目 博客好久没有更新了 GoF中定义: "将对象以树状结构组合,用以表现部分-全体的层次关系.组合模式让客户端在操作各个对象或组合时是一致的." 是一致的意思就 ...