Skip to content

Instantly share code, notes, and snippets.

@arkarkark
Last active April 13, 2022 17:08
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 arkarkark/3ccc9697650c6e97778de128e5a73b30 to your computer and use it in GitHub Desktop.
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.
#!/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