-
-
Save arkarkark/eef9bb9cfedbc6507a8255e543dd5d1e to your computer and use it in GitHub Desktop.
add imdb ratings to your itunes movie collection
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 put IMDB ratings into the bpm field of the selected movies in iTunes | |
# Also updates the MPAA Content rating from the IMDB data too | |
# If you want to update all the other metadata then I recommend https://metaz.io | |
# This lives at https://gist.github.com/arkarkark/eef9bb9cfedbc6507a8255e543dd5d1e | |
# Update it with | |
# gist -u eef9bb9cfedbc6507a8255e543dd5d1e ~/bin/share/add_imdb_movie_ratings | |
# gem install rb-scpt | |
# put an API key from http://www.omdbapi.com/apikey.aspx in ~/.omdbapikey | |
# brew install atomicparsley | |
# originally from: https://gist.github.com/catesandrew/942693/ | |
require 'rubygems' | |
require 'rb-scpt' | |
require 'ostruct' | |
require 'open-uri' | |
require 'open3' | |
require 'uri' | |
require 'json' | |
class LookupAPI | |
def initialize | |
@omdbapikey = File.open(File.join(Dir.home, ".omdbapikey")).read | |
end | |
def data(query) | |
query[:apikey] = @omdbapikey | |
url = "http://www.omdbapi.com/?#{URI.encode_www_form(query)}" | |
# puts url | |
json = URI.parse(url).open.read | |
JSON.parse(json, object_class: OpenStruct) | |
end | |
def search(query, year = nil) | |
params = { s: query } | |
params[:y] = year.to_s if year&.positive? | |
objs = data(params) | |
return search(query) if objs.Search.nil? && !year.nil? | |
puts "ERROR: #{objs.Error}" if objs.Error | |
objs.Search | |
end | |
def full_movie_info(imdb_id) | |
data(i: imdb_id) | |
end | |
def search_by_name_and_year(movie_name, movie_year) | |
movies = [] | |
movie_results = search(movie_name, movie_year) | |
movie_results = [] if movie_results.nil? | |
for movie_result in movie_results do | |
# check that the year tag in the file name matches with the release date, | |
# otherwise not the movie we are looking for | |
if movie_year&.positive? && movie_result.Year | |
if movie_result.Year.start_with?(movie_year.to_s) || | |
movie_result.Year.start_with?((movie_year.to_i + 1).to_s) || | |
movie_result.Year.start_with?((movie_year.to_i - 1).to_s) | |
movies.push(movie_result) | |
end | |
else | |
movies.push(movie_result) | |
end | |
end | |
movies | |
end | |
end | |
class Mp4Info | |
@@things_to_wait_for = [] | |
def initialize(filename) | |
@filename = filename | |
end | |
def content_rating | |
Open3.popen3('AtomicParsley', @filename, '-t') do |_stdin, stdout| | |
while (line = stdout.gets) | |
next unless line.include? 'com.apple.iTunes;iTunEXTC' | |
arr = line.split('contains:')[1].strip.split('|') | |
return OpenStruct.new( | |
org: arr[0], | |
content_rating: arr[1], | |
code: arr[2] | |
) | |
end | |
end | |
end | |
def content_rating=(rating) | |
puts "Setting content rating to #{rating}" | |
@@things_to_wait_for << Thread.new do | |
Open3.popen3( | |
'AtomicParsley', | |
@filename, | |
'--overWrite', | |
'--contentRating', | |
rating | |
) do |stdin, stdout, stderr| | |
stdin.close # make sure the subprocess is done | |
stdout.gets | |
stderr.gets | |
end | |
end | |
end | |
def self.finalize | |
return unless @@things_to_wait_for.length.positive? | |
puts 'Waiting for AtomicParsley to finish writing content ratings...' | |
@@things_to_wait_for.each(&:join) | |
end | |
end | |
class AddImdbRatings | |
include Appscript | |
def initialize | |
@itunes = app('iTunes') | |
@lookup_api = LookupAPI.new | |
end | |
def movie_choice(movie_name, movie_year = nil) | |
puts " Retrieving data from IMDb for #{movie_name} (#{movie_year})" | |
movies = @lookup_api.search_by_name_and_year(movie_name, movie_year) | |
if movies.length.zero? | |
puts " No matches found for \"#{movie_name}\" made in #{movie_year}" | |
puts '> Type a new search term, hit enter to skip,' | |
else | |
puts ' Potential Title Matches' | |
movie_counter = 0 | |
movies.each do |movie| | |
movie_type = " #{movie.Type}" if movie.Type != 'movie' | |
movie_extra = "(#{movie.Year}#{movie_type})" | |
movie_link = "https://imdb.com/title/#{movie.imdbID}" | |
puts " #{movie_counter}. #{movie_link} #{movie.Title} #{movie_extra}" | |
movie_counter += 1 | |
end | |
# ask user what movie to use | |
puts "> Type number of correct title (enter for first), a new search, 'no' to skip," | |
end | |
puts '> paste in an imdb url, or enter a score between 10 and 100' | |
movie_choice = gets.chomp | |
if movie_choice == '' | |
return if movies.length.zero? | |
movie_choice = '0' | |
end | |
return if movie_choice == 'no' | |
begin | |
movie_choice = Integer(movie_choice) | |
return OpenStruct.new(imdbRating: movie_choice * 0.1) if movie_choice > 10 && movie_choice <= 100 | |
return movies[movie_choice] | |
rescue ArgumentError | |
# It's not a number so it must be a new search term. | |
movie_choice.match(/(tt[0-9]+)/) { |m| return OpenStruct.new(imdbID: m[1]) } | |
return movie_choice(movie_choice) | |
end | |
end | |
def add_imdb_ratings_to_selection | |
@itunes.selection.get.each do |track| | |
puts "\n\n\n\n" | |
movie_name = track.name.get | |
movie_year = track.year.get | |
movie = movie_choice(movie_name, movie_year) | |
next unless movie | |
full_movie = movie | |
full_movie = @lookup_api.full_movie_info(movie.imdbID) if full_movie.imdbRating.nil? | |
# IMDB rating | |
rating = (full_movie.imdbRating.to_f * 10).floor | |
puts "Setting rating (bpm) to #{rating}" | |
track.bpm.set(rating) | |
# MPAA rating | |
next unless full_movie.Rated && !'N/A,Not Rated,Unrated'.split(',').include?(full_movie.Rated) | |
location = track.location.get.to_s | |
mp4info = Mp4Info.new(location) | |
content_rating = mp4info.content_rating | |
if content_rating.nil? || full_movie.Rated != content_rating.content_rating | |
mp4info.content_rating = full_movie.Rated | |
# @itunes.refresh(track), @itunes.selection.refresh, @itunes.add(location) NONE WORK! | |
end | |
end | |
Mp4Info.finalize | |
# now refresh the selections | |
Open3.popen3( | |
'/usr/bin/osascript', '-e', | |
'tell application "iTunes" to refresh selection' | |
) | |
end | |
end | |
AddImdbRatings.new.add_imdb_ratings_to_selection |
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 put IMDB ratings into the bpm field of the selected movies in iTunes | |
# Also updates the MPAA Content rating from the IMDB data too | |
# If you want to update all the other metadata then I recommend https://metaz.io | |
# This lives at https://gist.github.com/arkarkark/eef9bb9cfedbc6507a8255e543dd5d1e | |
# Update it with | |
# gist -u eef9bb9cfedbc6507a8255e543dd5d1e ~/bin/share/imdb_movie_ratings_adder | |
# gem install rb-scpt | |
# put an API key from http://www.omdbapi.com/apikey.aspx in ~/.omdbapikey | |
# brew install atomicparsley | |
# originally from: https://gist.github.com/catesandrew/942693/ | |
require 'rubygems' | |
require 'rb-scpt' | |
require 'ostruct' | |
require 'open-uri' | |
require 'open3' | |
require 'optparse' | |
require 'uri' | |
require 'json' | |
class LookupAPI | |
def initialize | |
@omdbapikey = File.open(File.join(Dir.home, ".omdbapikey")).read | |
end | |
def data(query) | |
query[:apikey] = @omdbapikey | |
url = "http://www.omdbapi.com/?#{URI.encode_www_form(query)}" | |
# puts url | |
json = URI.parse(url).open.read | |
JSON.parse(json, object_class: OpenStruct) | |
end | |
def search(query, year = nil) | |
params = { s: query } | |
params[:y] = year.to_s if year&.positive? | |
objs = data(params) | |
return search(query) if objs.Search.nil? && !year.nil? | |
puts "ERROR: #{objs.Error}" if objs.Error | |
objs.Search | |
end | |
def full_movie_info(imdb_id) | |
data(i: imdb_id) | |
end | |
def search_by_name_and_year(movie_name, movie_year) | |
movies = [] | |
movie_results = search(movie_name, movie_year) | |
movie_results = [] if movie_results.nil? | |
for movie_result in movie_results do | |
# check that the year tag in the file name matches with the release date, | |
# otherwise not the movie we are looking for | |
if movie_year&.positive? && movie_result.Year | |
if movie_result.Year.start_with?(movie_year.to_s) || | |
movie_result.Year.start_with?((movie_year.to_i + 1).to_s) || | |
movie_result.Year.start_with?((movie_year.to_i - 1).to_s) | |
movies.push(movie_result) | |
end | |
else | |
movies.push(movie_result) | |
end | |
end | |
movies | |
end | |
end | |
class Mp4Info | |
@@things_to_wait_for = [] | |
def initialize(filename) | |
@filename = filename | |
end | |
def content_rating | |
Open3.popen3('AtomicParsley', @filename, '-t') do |_stdin, stdout| | |
while (line = stdout.gets) | |
next unless line.include? 'com.apple.iTunes;iTunEXTC' | |
arr = line.split('contains:')[1].strip.split('|') | |
return OpenStruct.new( | |
org: arr[0], | |
content_rating: arr[1], | |
code: arr[2] | |
) | |
end | |
end | |
end | |
def content_rating=(rating) | |
puts "Setting content rating to #{rating}" | |
@@things_to_wait_for << Thread.new do | |
Open3.popen3( | |
'AtomicParsley', | |
@filename, | |
'--overWrite', | |
'--contentRating', | |
rating | |
) do |stdin, stdout, stderr| | |
stdin.close # make sure the subprocess is done | |
stdout.gets | |
stderr.gets | |
end | |
end | |
end | |
def self.finalize | |
return unless @@things_to_wait_for.length.positive? | |
puts 'Waiting for AtomicParsley to finish writing content ratings...' | |
@@things_to_wait_for.each(&:join) | |
end | |
end | |
class AddImdbRatings | |
include Appscript | |
def initialize | |
@itunes = app('iTunes') | |
@lookup_api = LookupAPI.new | |
end | |
def movie_choice(movie_name, movie_year = nil, all = false) | |
puts " Retrieving data from IMDb for #{movie_name} (#{movie_year})" | |
movies = @lookup_api.search_by_name_and_year(movie_name, movie_year) | |
if movies.length.zero? | |
puts " No matches found for \"#{movie_name}\" made in #{movie_year}" | |
return if all | |
puts '> Type a new search term, hit enter to skip,' | |
else | |
return movies[0] if all | |
puts ' Potential Title Matches' | |
movie_counter = 0 | |
movies.each do |movie| | |
movie_type = " #{movie.Type}" if movie.Type != 'movie' | |
movie_extra = "(#{movie.Year}#{movie_type})" | |
movie_link = "https://imdb.com/title/#{movie.imdbID}" | |
puts " #{movie_counter}. #{movie_link} #{movie.Title} #{movie_extra}" | |
movie_counter += 1 | |
end | |
# ask user what movie to use | |
puts "> Type number of correct title (enter for first), a new search, 'no' to skip," | |
end | |
puts '> paste in an imdb url, or enter a score between 10 and 100' | |
movie_choice = gets.chomp | |
if movie_choice == '' | |
return if movies.length.zero? | |
movie_choice = '0' | |
end | |
return if movie_choice == 'no' | |
begin | |
movie_choice = Integer(movie_choice) | |
return OpenStruct.new(imdbRating: movie_choice * 0.1) if movie_choice > 10 && movie_choice <= 100 | |
return movies[movie_choice] | |
rescue ArgumentError | |
# It's not a number so it must be a new search term. | |
movie_choice.match(/(tt[0-9]+)/) { |m| return OpenStruct.new(imdbID: m[1]) } | |
return movie_choice(movie_choice) | |
end | |
end | |
def add_imdb_ratings_to_selection(open: true, all: false, write: true) | |
@itunes.selection.get.each do |track| | |
puts "\n\n\n\n" if write or not all | |
movie_name = track.name.get | |
movie_year = track.year.get | |
movie = movie_choice(movie_name, movie_year, all) | |
next unless movie | |
Open3.popen3('open', 'https://www.imdb.com/title/' + movie.imdbID) if open | |
sleep(1) if open and all | |
full_movie = movie | |
full_movie = @lookup_api.full_movie_info(movie.imdbID) if full_movie.imdbRating.nil? | |
next unless write | |
# IMDB rating | |
rating = (full_movie.imdbRating.to_f * 10).floor | |
puts "Setting rating (bpm) to #{rating}" | |
track.bpm.set(rating) | |
# MPAA rating | |
next unless full_movie.Rated && !'N/A,Not Rated,Unrated'.split(',').include?(full_movie.Rated) | |
location = track.location.get.to_s | |
mp4info = Mp4Info.new(location) | |
content_rating = mp4info.content_rating | |
if content_rating.nil? || full_movie.Rated != content_rating.content_rating | |
mp4info.content_rating = full_movie.Rated | |
# @itunes.refresh(track), @itunes.selection.refresh, @itunes.add(location) NONE WORK! | |
end | |
end | |
Mp4Info.finalize | |
# now refresh the selections | |
Open3.popen3( | |
'/usr/bin/osascript', '-e', | |
'tell application "iTunes" to refresh selection' | |
) | |
end | |
end | |
all = false | |
open = true | |
write = true | |
OptionParser.new do |parser| | |
parser.on('-a', '--all') do |opt_all| | |
all = true | |
end | |
parser.on('--no-open') do |opt_open| | |
open = false | |
end | |
parser.on('-n', '--no-write') do |opt_no_write| | |
write = false | |
end | |
end.parse! | |
AddImdbRatings.new.add_imdb_ratings_to_selection(open: open, all: all, write: write) |
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
tell application "Terminal" | |
activate | |
set currentTab to do script "/Users/ark/bin/share/add_imdb_movie_ratings; exit 0" | |
end tell |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment