Skip to content

Instantly share code, notes, and snippets.

@Lanny
Last active January 2, 2016 02:18
Show Gist options
  • Save Lanny/8235682 to your computer and use it in GitHub Desktop.
Save Lanny/8235682 to your computer and use it in GitHub Desktop.
Utility to programmatically rename multiple tracks in iTunes using regular expressions.
import win32com.client
import re
from Tkinter import *
CHAR_WIDTH = 60
EXPL_TEXT = ('This is a little utility to rename track names in iTunes (on '
'Windows). To use it select some tracks in iTunes (click a track, '
'hold shift, click another track), enter match and replace '
'patterns and click "Execute Rename". Groups identified in the '
'match pattern will be available in the repl pattern. For example '
'match pattern: `\d+ (.+)` and repl pattern `\g<1>` will stip '
'any leading digits so "01 Wildlife Analysis" will be renamed '
'to "Wildlife Analysis".')
def justify(text, chars=CHAR_WIDTH):
ret = []
while len(text) > chars:
i = chars
while text[i] != ' ':
i -= 1
ret.append(text[:i])
text = text[i+1:]
ret.append(text)
return '\n'.join(ret)
def focus_next_window(event):
event.widget.tk_focusNext().focus()
return "break"
class Application(Frame):
@property
def _itunes(self):
"""Cache our itunes instance, no biggie"""
if not hasattr(self, '_itunes_inst'):
self._itunes_inst = win32com.client.Dispatch("iTunes.Application")
return self._itunes_inst
def do_rename(self):
"""Perform the rename on selected tracks"""
match_pat = self.match_pattern.get(1.0,END)[:-1]
rep_pat = self.rep_pattern.get(1.0,END)[:-1]
tracks = self._itunes.BrowserWindow.SelectedTracks
track_count = tracks.Count
for track_num in range(1, track_count+1) :
track = tracks.Item(track_num)
track.Name = re.sub(match_pat, rep_pat, track.Name)
def update_preview(self):
"""Read currently selected tracks, apply current regex substitutions, and
display the transformation in a "table" for the user to see."""
match_pat = self.match_pattern.get(1.0,END)[:-1]
rep_pat = self.rep_pattern.get(1.0,END)[:-1]
tracks = self._itunes.BrowserWindow.SelectedTracks
track_count = tracks.Count
origs = []
repls = []
for track_num in range(1, track_count+1) :
track = tracks.Item(track_num)
origs.append(track.Name)
repls.append(re.sub(match_pat, rep_pat, track.Name))
self.orig_data.set(' '.join(['"%s"' % s for s in origs]))
self.rep_data.set(' '.join(['"%s"' % s for s in repls]))
def createWidgets(self):
self.control_frame = Frame(self)
self.preview_frame = LabelFrame(self)
self.preview_frame['text'] = 'Preview'
self.button_frame = Frame(self.control_frame)
self.orig_col_label = Label(self.preview_frame)
self.orig_col_label['text'] = 'Before'
self.rep_col_label = Label(self.preview_frame)
self.rep_col_label['text'] = 'After'
self.orig_data = StringVar()
self.orig_col = Listbox(self.preview_frame)
self.orig_col['state'] = 'disabled'
self.orig_col['width'] = CHAR_WIDTH/2
self.orig_col['listvariable'] = self.orig_data
self.rep_data = StringVar()
self.rep_col = Listbox(self.preview_frame)
self.rep_col['state'] = 'disabled'
self.rep_col['width'] = CHAR_WIDTH/2
self.rep_col['listvariable'] = self.rep_data
self.QUIT = Button(self.button_frame)
self.QUIT["text"] = "QUIT"
self.QUIT["fg"] = "red"
self.QUIT["command"] = self.quit
self.rename_button = Button(self.button_frame)
self.rename_button["text"] = 'Execute Rename'
self.rename_button["command"] = self.do_rename
self.preview_button = Button(self.button_frame)
self.preview_button["text"] = 'Preview Changes'
self.preview_button["command"] = self.update_preview
self.match_label = Label(self.control_frame)
self.match_label['text'] = "Match Pattern"
self.match_pattern = Text(self.control_frame)
self.match_pattern.bind("<Tab>", focus_next_window)
self.match_pattern['height'] = 1
self.match_pattern['width'] = 60
self.rep_label = Label(self.control_frame)
self.rep_label['text'] = "Replacement Pattern"
self.rep_pattern = Text(self.control_frame)
self.rep_pattern.bind("<Tab>", focus_next_window)
self.rep_pattern['height'] = 1
self.rep_pattern['width'] = 60
self.expl_label = Label(self.control_frame)
self.expl_label['text'] = justify(EXPL_TEXT)
self.expl_label['width'] = CHAR_WIDTH
self.expl_label['justify'] = LEFT
self.control_frame.pack({'side': 'left'})
self.preview_frame.pack({'side': 'left'})
self.orig_col_label.grid(row=0, column=0, sticky=W)
self.orig_col.grid(row=1, column=0)
self.rep_col_label.grid(row=0, column=1, sticky=W)
self.rep_col.grid(row=1, column=1)
self.expl_label.pack({'side': 'top', 'anchor': 'w', 'expand':'true'})
self.match_label.pack({"side": "top"})
self.match_pattern.pack({"side": "top"})
self.rep_label.pack({"side": "top"})
self.rep_pattern.pack({"side": "top"})
self.button_frame.pack({'side': 'top'})
self.rename_button.pack({"side": "left"})
self.preview_button.pack({"side": "left"})
self.QUIT.pack({"side": "left"})
def __init__(self, master=None):
Frame.__init__(self, master)
self.pack()
self.createWidgets()
if __name__ == '__main__' :
root = Tk()
root.wm_title('iTunes Renaming Utility')
app = Application(root)
app.mainloop()
root.destroy()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment