Skip to content

Commit

Permalink
Initial pass
Browse files Browse the repository at this point in the history
  • Loading branch information
Matthew Nichols committed Mar 27, 2014
0 parents commit aa44372
Show file tree
Hide file tree
Showing 13 changed files with 408 additions and 0 deletions.
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
.DS_Store
.ruby-gemset
.ruby-version
Gemfile.lock
posters
17 changes: 17 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
source 'https://rubygems.org'

# Movie DBs
gem 'themoviedb'
gem 'imdb'

# mp4 Meta Data Writing
gem 'atomic-parsley-ruby'

# Help with searches
gem 'levenshtein'

# Watch for a new DVD insertion
gem 'rb-fsevent'

# Console IO
gem 'highline'
19 changes: 19 additions & 0 deletions README.markdown
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
Install:

brew install AtomicParsley
brew install exiftool
brew install tag


Notes:

http://manpages.ubuntu.com/manpages/hardy/man1/AtomicParsley.1.html
https://github.com/cparratto/atomic-parsley-ruby

http://www.sno.phy.queensu.ca/~phil/exiftool/
http://miniexiftool.rubyforge.org

https://github.com/jdberry/tag

https://github.com/ahmetabdi/themoviedb
https://github.com/ariejan/imdb
18 changes: 18 additions & 0 deletions config.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
require 'thread'
# require 'fsevent'
require 'rb-fsevent'
require 'highline/import'
require 'atomic-parsley-ruby'

require './movie_extensions'
require './search'
require './dvd'
require './movie'

@api_key = "867c178725a7e646a0f108ee78781a49"
WORKING_DIR = "/Users/mnichols/Movies/Ripping"
DEST_DIR = "/Users/mnichols/Movies/Ripped"
POSTER_PATH = "./Posters"
DISTANCE_THRESHOLD = 0.2

Tmdb::Api.key(@api_key)
45 changes: 45 additions & 0 deletions dvd.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
class Dvd
attr_accessor :volumes

def initialize
@volumes = "/Volumes"
end

def volume
vol = nil
Dir.entries(@volumes).each do |video_dir|
if not [".", ".."].member?(video_dir)
full_path = File.join(@volumes, video_dir)
if Dir.exists?(File.join(full_path, "VIDEO_TS"))
vol = full_path
break
end
end
end
vol
end

def title
raise "Invalid DVD Root or DVD not mounted at #{@volumes}" if not present?
File.basename(volume).gsub("_", " ").capitalize
end

def present?
!volume.nil?
end

def rip(output)
File.delete(output) if File.exists?(output)

system("HandBrakeCLI -Z \"AppleTV 3\" --main-feature -i \"#{volume}\" -o \"#{output}\"")
exit_code = $?
if exit_code.exitstatus != 0
`killall HandBrakeCLI`
Kernel.exit(exit_code.exitstatus)
end
end

def eject
`drutil tray eject`
end
end
55 changes: 55 additions & 0 deletions movie.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
class Movie
extend Forwardable

def initialize(tmdb_info, imdb_info)
@tmdb_info = tmdb_info
@imdb_info = imdb_info
end

def_delegators :@tmdb_info, :title, :year, :poster_path
def_delegators :@imdb_info, :plot, :genres, :plot_summary

require 'open-uri'

def tag(output)
Dir.mkdir(POSTER_PATH) if not Dir.exists?(POSTER_PATH)
poster_file_path = File.expand_path(File.join(POSTER_PATH, poster_path))
poster = File.open(poster_file_path, "wb")
begin
open("http://image.tmdb.org/t/p/w500#{poster_path}", "rb") do |download|
poster.write(download.read)
end
ensure
poster.close()
end

# Remove stik and other artwork, just in case
v = AtomicParsleyRuby::Media.new(output)
v.artwork "REMOVE_ALL"
v.stik "remove"
v.overwrite true
v.process

v = AtomicParsleyRuby::Media.new(output)
v.overwrite true
v.title title
v.comment plot unless plot.nil?
v.description plot unless plot.nil?
v.year year.to_s
v.stik "0"
v.genre genres[0]
v.longdesc plot_summary unless plot_summary.nil?
v.artwork poster_file_path
v.process

# m = MiniExiftool.new output
# m["director"] = info.director

# Need to use mp4v2 lib extensions to write this stuff
# m["directors//name"] = "Bob"
# m["producers//name"] = "George"
# m["screenwriters//name"] = "Writers"
# m["studio//name"] = "Studio C"
# m.save
end
end
13 changes: 13 additions & 0 deletions movie_extensions.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
module MovieExtensions
def title_distance
@title_distance
end

def title_distance=(distance)
@title_distance = distance
end

def year
Date.parse(release_date).year
end
end
109 changes: 109 additions & 0 deletions run.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
#!/usr/bin/ruby

require './config.rb'
require 'fileutils'
require 'io/console'

@command = nil
@last_volume = ""
@dvd = Dvd.new

def process_dvd
puts
puts "--------------------------------------------------------------"
puts "-- Volume: #{@dvd.volume}"
puts "-- Title: #{@dvd.title}"
puts "--------------------------------------------------------------"
puts

searcher = Search.new
movie = searcher.closest(@dvd.title)
title = "#{movie.title} (#{movie.year})"
puts WORKING_DIR
puts DEST_DIR

output = File.join(WORKING_DIR, "#{title.gsub(": ", " - ").gsub(":", " - ")}.m4v")
puts
puts "--------------------------------------------------------------"
puts "-- Title: #{movie.title}"
puts "-- File: #{output}"
puts "--------------------------------------------------------------"
puts
continue = ask("Continue? (y/n)") { |q| q.default = "y" }
if continue != 'y'
puts "Aborting..."
return
end

puts
puts "Ripping..."
puts
puts

@dvd.rip(output)
movie.tag(output)

puts
say "\"#{output}\" complete"
puts
puts
system("drutil tray eject")
FileUtils.mv(output, DEST_DIR)

# puts `exiftool "#{output}"`
end

fsevent = FSEvent.new
fsevent.watch %W(/Volumes) do |directories|
if @command.nil? and @dvd.present? and (@last_volume != @dvd.volume)
@last_volume = @dvd.volume
@command = "r"
end
end

user_command = Thread.new do
while true
if @command.nil?
puts "Enter command (r|q) or insert DVD:"
@command = STDIN.getch.chomp
end
sleep 1
end
end

Signal.trap("INT") do
@command = "q"
end

message_thread = Thread.new do
while true
begin
while @command.nil?
sleep 1
end
if @command == "r"
process_dvd
end
if @command == "q"
puts "Quitting..."
fsevent.stop
break
end
rescue Exception => e
puts e.message
puts e.backtrace
ensure
@command = nil
end
end
end

fsevent.run

message_thread.kill
message_thread.join

user_command.kill
user_command.join

exit 0
55 changes: 55 additions & 0 deletions search.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
require './search_factory'
require 'levenshtein'

class Search
def closest(title)
searcher = SearchFactory.get("tmdb")

movies = leven_sort(title, searcher.search(title))
while movies.empty?
puts "Unable to locate title"
puts "Enter movie title:"
title = gets.chomp
movies = leven_sort(title, searcher.search(title))
end

movie = auto_select(movies)
if movie.nil? or ambiguous?(movies)
puts "Please choose:"
movies.each_with_index do |movie, i|
puts "#{i}. #{movie.title} (#{movie.release_date}) (#{movie.title_distance.round(3)})"
end
title_sel = gets
movie = movies[title_sel.to_i]
end
info = SearchFactory.get("imdb").search(title)[0]

movie = Movie.new(movie, info)
movie
end

def leven_sort(search_title, movies)
movies.each do |movie|
distance = Levenshtein.normalized_distance(search_title, movie.title)
movie.title_distance = distance
end
movies.reverse.sort_by { |m| m.title_distance }
end

def auto_select(movies)
return movies[0] if (not movies.empty?) and (movies[0].title_distance < DISTANCE_THRESHOLD)
nil
end

def ambiguous?(movies)
return true if movies.empty?
return false if movies.size == 1

lowest_distance = movies[0].title_distance

return true if lowest_distance > DISTANCE_THRESHOLD
return true if movies.find_all { |m| m.title_distance == lowest_distance }.size > 1

false
end
end
10 changes: 10 additions & 0 deletions search_factory.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
require './search_imdb'
require './search_tmdb'

class SearchFactory
def self.get(name)
return SearchImdb.new if name == 'imdb'
return SearchTmdb.new if name == 'tmdb'
raise "Invalid Search Engine Name"
end
end
11 changes: 11 additions & 0 deletions search_imdb.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
require 'imdb'

class SearchImdb
def search(title)
Imdb::Search.new(title).movies
end
end

class Imdb::Movie
include MovieExtensions
end
11 changes: 11 additions & 0 deletions search_tmdb.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
require 'themoviedb'

class SearchTmdb
def search(title)
Tmdb::Movie.find(title)
end
end

class Tmdb::Movie
include MovieExtensions
end
Loading

0 comments on commit aa44372

Please sign in to comment.