Skip to content

Instantly share code, notes, and snippets.

@mhl
Created November 17, 2009 14:29
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mhl/236933 to your computer and use it in GitHub Desktop.
Save mhl/236933 to your computer and use it in GitHub Desktop.
Most recent podcasts playlist
#!/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