Created
November 17, 2009 14:29
-
-
Save mhl/236933 to your computer and use it in GitHub Desktop.
Most recent podcasts playlist
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/python2.5 | |
import sys | |
import os | |
import stat | |
import re | |
from subprocess import call, check_call, Popen, PIPE | |
from optparse import OptionParser | |
# I've found this script useful for copying my most recently | |
# downloaded podcasts to a Sansa Clip MP3 player. It creates a | |
# recent-podcasts.m3u playlist in the destination directory which | |
# lists these podcasts from oldest to newest. | |
# | |
# The only subtlety is that if you specify the --mangle option then the | |
# script will wipe out any "podcast" TCON id3v2 tag in the version of | |
# the file on the device, since this prevents podcasts appearing in a | |
# playlist under "MUSIC" on my device. | |
# | |
# I'd recommend creating an alias in your .bashrc file to call this | |
# script with your usual arguments. | |
usage_message = "Usage: %prog [OPTIONS] [SOURCE_DIRECTORY] [DESTINATION_DIRECTORY]" | |
parser = OptionParser(usage=usage_message) | |
parser.add_option('-n', | |
dest="n", | |
default=40, | |
type="int", | |
help="maximum number of podcasts [default %default]") | |
parser.add_option('-m', '--mangle', | |
action="store_true", | |
dest="mangle", help="wipe out the TCON tag if it says 'podcast'") | |
options,args = parser.parse_args() | |
if len(args) != 2: | |
parser.print_help() | |
sys.exit(1) | |
new_podcasts_source, destination_podcasts_folder = args | |
def get_content_field(mp3_filename): | |
command = ["id3v2","-l",mp3_filename] | |
result = Popen(command, stdout=PIPE).communicate()[0] | |
m = re.search("(?ims)(TCON?) \([^\)]+\): ([^\r\n]*)[\r\n]",result) | |
return m and (m.group(1),m.group(2)) | |
def length_in_seconds(mp3_filename): | |
command = ["mp3info", "-p", "%S", mp3_filename] | |
result = Popen(command, stdout=PIPE, stderr=PIPE).communicate()[0] | |
return int(result.strip(),10) | |
def title_id3v2(mp3_filename): | |
command = ["id3v2","-l",mp3_filename] | |
result = Popen(command, stdout=PIPE).communicate()[0] | |
# The obsolete v2.2 uses TT2 instead of TIT2: | |
m = re.search("(?ims)TI?T2 \([^\)]+\): ([^\r\n]*)[\r\n]",result) | |
return m and m.group(1) | |
def title_id3v1(mp3_filename): | |
command = ["mp3info", "-p", "%t", mp3_filename] | |
result = Popen(command, stdout=PIPE, stderr=PIPE).communicate()[0].strip() | |
return len(result) >= 1 and result | |
def title_from_filename(mp3_filename): | |
return re.sub('\.([^\.]*)$','',os.path.basename(mp3_filename)) | |
files = [] | |
audio_file_regexp = re.compile(".mp3$",re.I) | |
for root, subfolders, basenames in os.walk(new_podcasts_source): | |
for file in basenames: | |
if audio_file_regexp.search(file): | |
files.append(os.path.join(root,file)) | |
def mtime(filename): | |
return os.stat(filename)[stat.ST_MTIME] | |
files.sort(key=mtime) | |
most_recent_files = files[-options.n:len(files)] | |
playlist_file = os.path.join(destination_podcasts_folder,"recent-podcasts.m3u") | |
fp = open(playlist_file,"w") | |
fp.write("#EXTM3U\r\n") | |
for file in most_recent_files: | |
t1 = title_id3v1(file) | |
t2 = title_id3v2(file) | |
t_default = title_from_filename(file) | |
t = t2 or t1 or t_default | |
if not t: | |
print "Failed to find a title for: '"+file+"', so using 'Unknown'" | |
t = "Unknown" | |
l = length_in_seconds(file) | |
basename = os.path.basename(file) | |
destination = os.path.join(destination_podcasts_folder,basename) | |
fp.write("#EXTINF:"+str(l)+","+t+"\r\n") | |
fp.write(basename+"\r\n") | |
if os.path.exists(destination): | |
print "Skipping "+basename+"; plausibly already copied" | |
continue | |
print "Copying "+basename | |
if 0 != call(["cp", | |
file, | |
destination]): | |
print "Copy failed: deleting the partial destination file and exiting" | |
call(["rm","-f",destination]) | |
sys.exit(1) | |
if options.mangle: | |
# Annoyingly, my Sansa Clip won't list files in a playlist if | |
# they have a TCON or TCO tag that says that it's a podcast, | |
# so if that tag is there, rewrite it to be empty: | |
content_tag = get_content_field(destination) | |
if content_tag: | |
tag, value = content_tag | |
if re.search("(?ims)podcast",value): | |
print " Fixing "+tag+" tag for "+basename | |
print " "+tag+" was: '"+value+"'" | |
check_call(["id3v2", | |
"--TCON", | |
"", | |
destination]) | |
fp.close() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment