2010年4月18日 星期日

[Script] Split audio files by cue

自從去年那篇 Split(Rename) audio files by cue files 後,覺得還要一個一個打指令實在太麻煩了,就自己寫個 script 來完成全部步驟,所需 package 相同, 只是要多加一個 ffmpeg 來幫 ape 轉檔,shnsplit 內建是 mac ,但是原作者網站都掛了,AUR 上又連到 gentoo 的軟體包去抓,實在很怪,就用 ffmpeg 來轉,如果 cue 是 UTF-8 的話會移除 BOM 再餵給 cuebreakpoint 才不會出錯,分割完後使用 cuetag.sh 設定 tag 資訊,但只支援 id3 和 vorbis,最後再改檔名。

下載

內容:

#! /usr/bin/env python
# -*- coding: utf-8 -*-
# require: ffmpeg, cuebreakpoint, cueprint, cuetag.sh , shnsplit
# use ffmpeg to convert musics

import sys
import os
import glob
import getopt
import subprocess as sp

###### usage ######
usage = """ \
Usage :
-c cuefile: the cue files to be used
-i file : the music file to be split
-o format : specify the format to be converted
"""

###### main ######

def parse_opt():
c_file = m_file = format = ""
try:
opts, args = getopt.getopt( sys.argv[1:],"c:i:o:" )
for o in opts:
if o[0] == "-c":
c_file = o[1]
elif o[0] == "-i":
m_file = o[1]
elif o[0] == "-o":
format = o[1]

# raise Error if not correct args
for f in [ c_file, m_file, format]:
if f == "": raise ValueError

return c_file ,m_file ,format

except ( getopt.GetoptError, ValueError ):
print usage
sys.exit()

def try_cmd( cmd_arg ):
# error strings
error = ["error", "Error", "ERROR"]
try:
ret = sp.Popen([ cmd_arg ],stdout=sp.PIPE,stderr=sp.PIPE,\
shell=True).communicate()
# if error strings passed to stdout or stderror
# raise error and stop the script
for es in error:
if ( es in ret[0] ) or ( es in ret[1]):
raise OSError( "\n\nExecution failed !!!\n" +\
"error messages :\n\n" +\
ret[0] + ret [1] )
return ret[0]
except OSError, e:
raise

def track_rename( c_file ):
track_names = try_cmd("cueprint -t '%02n. %t\n' " + c_file)
track_names = track_names.split('\n')
tracks = glob.glob("./split_cue_tmp_*")
tracks = sorted( tracks )
file_type = tracks[0].rpartition('.')[2]
zf = zip(tracks, track_names)
for t_name in zf:
print t_name[0], t_name[1]
os.rename( t_name[0], t_name[1] + '.' + file_type)

def remove_BOM( utf8_file ):
with open(utf8_file,'r') as f:
content = f.readlines()

check_c = map( ord, [ c for c in content[0][0:3] ] )

if check_c == [239, 187, 191] :
print "There are BOM inside!"
content[0] = content[0][3:]
with open(utf8_file + ".tmp.cue" , 'w') as f:
f.writelines(content)
return utf8_file + ".tmp.cue"
else:
print "File with no BOM"
return utf8_file

def check_format( m_file, format ):
if format == "":
return False
f_string = [format.lower(), format.upper(), format.capitalize()]
f_info = try_cmd("file " + m_file)
print f_info
for s in f_string:
if s in f_info:
print "Orginal file format is the same as specified !!"
return True
# different file format, need conversion
return False

def split_tracks(c_file, m_file, format):
# mac not installed , use ffmpeg to convert ape
if check_format( m_file, "ape"):
new_m_file = m_file.rpartition('.')[0] + '.' + format.lower()
try_cmd("ffmpeg -i " + m_file + " " + new_m_file)
m_file = new_m_file

# cuebreakpoints |shnsplit
s_args = " -a split_cue_tmp_ -o " + format + " "
try_cmd("cuebreakpoints " + c_file + "|shnsplit " + s_args + m_file)
# use cuetag.sh to add tags (only for vorbis and id3)
try_cmd("cuetag.sh " + c_file + " split_cue_tmp_*")

def main():
c_file, m_file, format = parse_opt()
c_file = remove_BOM( c_file )
split_tracks(c_file, m_file, format)
track_rename(c_file)


if __name__ == "__main__":
main()