Last active
April 13, 2022 17:08
-
-
Save arkarkark/3ccc9697650c6e97778de128e5a73b30 to your computer and use it in GitHub Desktop.
A Small script to update the currently selected iTunes tracks with various criteria.
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
#!/usr/bin/env ruby | |
# Copyright 2019 Alex K (wtwf.com) | |
# A Small script to update the currently selected iTunes tracks with various criteria. | |
# This lives at https://gist.github.com/arkarkark/3ccc9697650c6e97778de128e5a73b30 | |
# Update it with | |
# gist -u 3ccc9697650c6e97778de128e5a73b30 ~/bin/share/update_itunes_selection | |
# possible columns for your regex names | |
# | |
COLUMNS = { | |
album: "Album", | |
album_artist: "Album Artist", | |
album_rating: "Album Rating", | |
artist: "Artist", | |
bpm: "Beats Per Minute", | |
bit_rate: "Bit Rate", | |
category: "Category", | |
comment: "Comments", | |
composer: "Composer", | |
date_added: "Date Added", | |
modification_date: "Date Modified", | |
description: "Description", | |
disc_number: "Disc Number", | |
disc_count: "Disc Count", | |
episode_ID: "Episode ID", | |
episode_number: "Episode Number", | |
EQ: "Equalizer", | |
genre: "Genre", | |
grouping: "Grouping", | |
cloud_status: "iCloud Download", | |
kind: "Kind", | |
played_date: "Last Played", | |
skipped_date: "Last Skipped", | |
loved: "Love", | |
movement: "Movement Name", | |
movement_number: "Movement Number", | |
movement_count: "Movement Count", | |
played_count: "Plays", | |
rating: "Rating", | |
release_date: "Release Date", | |
sample_rate: "Sample Rate", | |
season_number: "Season", | |
show: "Show", | |
size: "Size", | |
skipped_count: "Skips", | |
sort_album: "Sort Album", | |
sort_album_artist: "Sort Album Artist", | |
sort_artist: "Sort Artist", | |
sort_composer: "Sort Composer", | |
sort_name: "Sort Name", | |
sort_show: "Sort Show", | |
time: "Time", | |
track_count: "Track Count", | |
track_number: "Track Number", | |
work: "Work", | |
year: "Year" | |
} | |
# gem install rb-scpt | |
require 'rubygems' | |
require 'rb-scpt' | |
require 'json' | |
require 'ostruct' | |
require 'open3' | |
require 'optparse' | |
require 'uri' | |
$opt_update = true | |
$opt_force = false | |
$opt_field = "name" | |
class ArtistName | |
def initialize | |
system("stty raw -echo") | |
print "Type 'a' if the artist is first," | |
print "'s' if the artist is second" | |
print "'q' to quit, any other key to skip\n" | |
end | |
def update(track) | |
name = track.send($opt_field).get | |
first, second = name.split(/\s*-\s*/, 2) | |
printf("\r\n%30s -- %s", first, second) | |
key = STDIN.getc | |
raise "All Done" if key == 'q' | |
return unless $opt_update | |
if key == 'a' then | |
track.send($opt_field).set(second) | |
track.artist.set(first) | |
elsif key == 's' then | |
track.send($opt_field).set(first) | |
track.artist.set(second) | |
end | |
end | |
def done | |
system("stty -raw echo") | |
end | |
end | |
# useful things to match (p(track.methods) to get ALL the values) | |
# disc_number, track_number, album, artist, name | |
class RegexMatcher | |
def initialize(pattern) | |
@pattern = pattern | |
end | |
def update(track) | |
name = track.public_send($opt_field).get | |
p name | |
name.match(@pattern) { |m| | |
m.names.each { |match_name| | |
next unless m[match_name] | |
if $opt_force or | |
match_name == $opt_force or | |
["", 0].include? track.public_send(match_name).get | |
printf("%15s '%s'\r\n", match_name, m[match_name]) | |
track.public_send(match_name).set(m[match_name]) if $opt_update | |
end | |
} | |
} | |
track.comment.set(name) unless not $opt_force or track.comment.get | |
end | |
end | |
class UrlUnescape | |
def update(track) | |
orig = track.send($opt_field).get | |
new_value = URI.unescape(orig) | |
printf("%-20s -> %s\r\n", orig, new_value) | |
track.send($opt_field).set(new_value) if $opt_update | |
end | |
end | |
class Capitalize | |
def update(track) | |
orig = track.send($opt_field).get | |
new_value = orig.split.map(&:capitalize)*' ' | |
printf("%-20s -> %s\r\n", orig, new_value) | |
track.send($opt_field).set(new_value) if $opt_update | |
end | |
end | |
class UnderscoresToSpaces | |
def update(track) | |
orig = track.send($opt_field).get | |
new_value = orig.gsub('_', ' ').gsub(/\s+/, ' ').strip | |
printf("%-20s -> %s\r\n", orig, new_value) | |
track.send($opt_field).set(new_value) if $opt_update | |
end | |
end | |
class UpdateSelection | |
include Appscript | |
def initialize(helper) | |
@itunes = app('iTunes') | |
@itunes.selection.get.each do |track| | |
helper.update(track) | |
end | |
helper.done() if defined? helper.done | |
end | |
end | |
# RegexMatcher.new(/(?<album>Disneyland_Audio_Tour)_Chapter_6_CH(?<disc_number>6)_TRK(?<track_number>\d+)_(?<name>.*)/) | |
helper = nil | |
OptionParser.new do |parser| | |
parser.on('-n', '--test') do |opt_all| | |
$opt_update = false | |
end | |
parser.on('-f', '--force') do |opt_open| | |
$opt_force = true | |
end | |
parser.on("--field=") do |opt_field| | |
$opt_field = opt_field | |
end | |
parser.on('--artist-song') do |opt_no_write| | |
helper = RegexMatcher.new(/^(?<artist>.*?)\s*-+\s*(?<name>.*)/) | |
end | |
parser.on('--song-artist') do |opt_no_write| | |
helper = RegexMatcher.new(/^(?<name>.*?)\s*-\s*(?<artist>.*)/) | |
end | |
parser.on('--track-numbers') do |opt_no_write| | |
helper = RegexMatcher.new(/^[\s\.-]*(?:(?<disc_number>\d+)-)*(?<track_number>\d+)[\s\.-]*(?<name>.*)/) | |
end | |
parser.on('--url-unescape') do |opt_no_write| | |
helper = UrlUnescape.new | |
end | |
parser.on('--capitalize') do |opt_no_write| | |
helper = Capitalize.new | |
end | |
parser.on('--re=') do |opt_re| | |
helper = RegexMatcher.new(Regexp.new(opt_re)) | |
end | |
parser.on('--remove-1') do |opt_re| | |
helper = RegexMatcher.new(/^(?<name>.*?)(\s+1)+$/) | |
end | |
parser.on('--underscores-to-spaces') do |opt_re| | |
helper = UnderscoresToSpaces.new | |
end | |
parser.on('--season-episode') do |opt_re| | |
helper = RegexMatcher.new(/(S(?<season_number>\d+))?EP?(?<episode_number>\d+)/) | |
end | |
parser.on('--youtube-id-at-end') do |opt_re| | |
helper = RegexMatcher.new(/^(?<name>.*)-(?<comment>[A-Za-z0-9_-]{11})$/) | |
end | |
parser.on("-h", "--help", "Prints this help") do | |
puts parser | |
exit | |
end | |
end.parse! | |
if not helper | |
print "You need to choose a mode\n\n" | |
exit | |
end | |
UpdateSelection.new(helper) | |
# now refresh the selections | |
Open3.popen3( | |
'/usr/bin/osascript', '-e', | |
'tell application "iTunes" to refresh selection' | |
) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment