Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
add imdb ratings to your itunes movie collection
#!/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
#!/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)
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