Last active
January 2, 2016 02:18
-
-
Save Lanny/8235682 to your computer and use it in GitHub Desktop.
Utility to programmatically rename multiple tracks in iTunes using regular expressions.
This file contains 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
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