class MovieManagerGem::Finder

DH Searcher ###

Public Class Methods

new() click to toggle source

require ‘pg’ # postgres implementation

# File lib/movie-manager-gem.rb, line 13
def initialize()
        # start database
        @@DB = Sequel.sqlite(File.expand_path('~/.movie_manager_003.db'))    # sqlite3 implementation
        # @@DB = Sequel.postgres('testdb', :host=>'localhost', :user=>'David', :password=>'password')        # postgres implementation

        # create tables within database
        create_all_tables

        # tie ruby-accessible datasets to database table
        @movies_dataset                                              = @@DB[:movies]
        @movie_genre_dataset                 = @@DB[:movie_genre]
        @genres_dataset                                              = @@DB[:genres]
        @directories_dataset                 = @@DB[:directories]
        @movie_actor_dataset                 = @@DB[:movie_actor]
        @actors_dataset                                              = @@DB[:actors]
        @movie_director_dataset              = @@DB[:movie_director]
        @directors_dataset                           = @@DB[:directors]

        # joins
        @movie_directories_join      = @movies_dataset.join(:directories, :id => :directory_id) # must be before other joins
        @movie_genre_join                                    = @movie_directories_join.join(:movie_genre, :movie_id => :movies__id).join(:genres, :id => :genre_id)
        @movie_actor_join                                    = @movie_directories_join.join(:movie_actor, :movie_id => :movies__id).join(:actors, :id => :actor_id)
        @movie_director_join                 = @movie_directories_join.join(:movie_director, :movie_id => :movies__id).join(:directors, :id => :movie_director__director_id)

        # queues and threads
        @local_movies_queue                          = Queue.new
        @processed_movies_queue              = Queue.new
        @threads                                                                     = []
end

Public Instance Methods

actors_record_exists?(name) click to toggle source
# File lib/movie-manager-gem.rb, line 368
def actors_record_exists?(name)        
        return false if @actors_dataset.select(:id).where(:name => name).all.length == 0 
        return true
end
add_all_movies_to_table() click to toggle source
# File lib/movie-manager-gem.rb, line 284
def add_all_movies_to_table   # works
# add func to auto-find/add new movies.
        2.times do
                # this code was supplied by Theo on SO
                # http://stackoverflow.com/questions/6558828/thread-and-queue
                @threads << Thread.new do
                        until @local_movies_queue.empty?
                                long_name, clean_name, data = @local_movies_queue.pop(true) rescue nil
                                if long_name
                                        if movies_record_exists?(long_name)
                                                movie_title = @movies_dataset.select(:title).where(:original_title => long_name).first[:title]
                                                puts "#{movie_title} record already exists"
                                        else
                                                data = get_imdb_movie_info(clean_name)
                                                @processed_movies_queue << [long_name, clean_name, data]
                                                add_movie(@processed_movies_queue.pop)
                                        end
                                end
                        end
                end
        end
        @threads.each { |t| t.join } 
end
add_movie((long_name, clean_name, data)) click to toggle source
# File lib/movie-manager-gem.rb, line 308
def add_movie((long_name, clean_name, data)) # works
        if data
                # Add Directory to directories_dataset
                @directories_dataset.insert(:directory_path =>  File.dirname(long_name)) unless directories_record_exists?(File.dirname(long_name))

                # Add Movie to movies_dataset
                @movies_dataset.insert( :original_title => long_name, 
                        :title => data.title[0..-8],
                        :imdb_score => data.rating,
                        :summary => data.plot_summary||'No summary available',
                        :length => data.length||-1,
                        :date_added => Time.new(),
                        :directory_id => @directories_dataset.select(:id).where(:directory_path => File.dirname(long_name)).first[:id])

                movie_id = @movies_dataset.select(:id).where(:title => data.title[0..-8]).first[:id]

                # Add Actors to actors_dataset and movie_actor_dataset
                data.cast_members.each do |actor| 
                        @actors_dataset.insert(:name => actor) unless actors_record_exists?(actor) # used to be :name => actor.name. Make sure this works online!
                        @movie_actor_dataset.insert(:movie_id => movie_id,
                                :actor_id => @actors_dataset.select(:id).where(:name => actor).first[:id])
                end if data.cast_members

                # Add Genres to genres_dataset and movie_genre_dataset
                data.genres.each do |genre| 
                        @genres_dataset.insert(:genre => genre) unless genres_record_exists?(genre)
                        @movie_genre_dataset.insert(:movie_id => movie_id,
                                :genre_id => @genres_dataset.select(:id).where(:genre => genre).first[:id])
                end if data.genres

                # Add Directors to directors_dataset and movie_director_dataset
                data.director.each do |director|  
                        if director != "(more)"    # compensates for a bug in imdb gem. Pull request submitted.
                                @directors_dataset.insert(:name => director) unless directors_record_exists?(director)
                                @movie_director_dataset.insert(:movie_id => movie_id,
                                        :director_id => @directors_dataset.select(:id).where(:name => director).first[:id])
                        end
                end if data.director

                puts "#{data.title[0..-8]} added to table" 

        else
                @movies_dataset.insert(:original_title => long_name, :title => clean_name, :date_added => Time.new())
                puts "#{clean_name} added to table"
        end
end
create_actors_table() click to toggle source
# File lib/movie-manager-gem.rb, line 459
def create_actors_table
        if @@DB.table_exists?(:actors)
                raise StandardError, 'Actors table already exists, try a different name'    
        else
                @@DB.create_table :actors do
                        primary_key :id
                        String :name
                end
        end
end
create_all_tables() click to toggle source

Tables & DBs ###

# File lib/movie-manager-gem.rb, line 389
def create_all_tables
        create_directories_table              unless @@DB.table_exists?(:directories)

        create_movies_table                           unless @@DB.table_exists?(:movies)

        create_genres_table                           unless @@DB.table_exists?(:genres)
        create_movie_genre_table              unless @@DB.table_exists?(:movie_genre)

        create_actors_table                           unless @@DB.table_exists?(:actors)
        create_movie_actor_table              unless @@DB.table_exists?(:movie_actor)

        create_directors_table                        unless @@DB.table_exists?(:directors)
        create_movie_director_table unless @@DB.table_exists?(:movie_director)
end
create_directories_table() click to toggle source
# File lib/movie-manager-gem.rb, line 424
def create_directories_table
        if @@DB.table_exists?(:directories)
                raise StandardError, 'Directories table already exists, try a different name'       
        else
                @@DB.create_table :directories do
                        primary_key :id
                        String :directory_path
                        Integer :available,                :default => 1
                end 
        end
end
create_directors_table() click to toggle source
# File lib/movie-manager-gem.rb, line 482
def create_directors_table 
        if @@DB.table_exists?(:directors)
                raise StandardError, 'Directors table already exists, try a different name' 
        else
                @@DB.create_table :directors do
                        primary_key :id
                        String :name
                end
        end
end
create_genres_table() click to toggle source
# File lib/movie-manager-gem.rb, line 436
def create_genres_table
        if @@DB.table_exists?(:genres)
                raise StandardError, 'Genres table already exists, try a different name'    
        else
                @@DB.create_table :genres do
                        primary_key :id
                        String :genre
                end
        end
end
create_movie_actor_table() click to toggle source
# File lib/movie-manager-gem.rb, line 470
def create_movie_actor_table
        if @@DB.table_exists?(:movie_actor)
                raise StandardError, 'Movie_Actor table already exists, try a different name'       
        else
                @@DB.create_table :movie_actor do
                        primary_key :id
                        Integer :movie_id
                        Integer :actor_id
                end
        end
end
create_movie_director_table() click to toggle source
# File lib/movie-manager-gem.rb, line 493
def create_movie_director_table
        if @@DB.table_exists?(:movie_director)
                raise StandardError, 'Movie_Director table already exists, try a different name'    
        else
                @@DB.create_table :movie_director do
                        primary_key :id
                        Integer :movie_id
                        Integer :director_id
                end
        end
end
create_movie_genre_table() click to toggle source
# File lib/movie-manager-gem.rb, line 447
def create_movie_genre_table
        if @@DB.table_exists?(:movie_genre)
                raise StandardError, 'Movie_Genre table already exists, try a different name'       
        else
                @@DB.create_table :movie_genre do
                        primary_key :id
                        Integer :movie_id
                        Integer :genre_id
                end
        end
end
create_movies_table() click to toggle source
# File lib/movie-manager-gem.rb, line 404
def create_movies_table
        if @@DB.table_exists?(:movies)
                raise StandardError, 'Movies table already exists, try a different name'     
        else
                @@DB.create_table :movies do
                        primary_key :id
                        String      :original_title
                        String      :title
                  Float                      :imdb_score,                               :default => -1    #1-100
                  Float              :my_score,                                         :default => -1     #1-100
                  Integer    :correct_filename,        :default => 0         #0/no, 1/yes
                  Integer    :watched,                                         :default => -1     #-1/unknown, 0/no, 1/yes
                  String             :summary,                                         {:text => true, :default => 'No summary available.'}
                  Integer    :length,                                          :default => -1
                  String     :date_added
                  Integer    :directory_id 
                        end 
                end
        end
directories_record_exists?(path) click to toggle source
# File lib/movie-manager-gem.rb, line 383
def directories_record_exists?(path)
        return false if @directories_dataset.select(:directory_path).where(:directory_path => path).all.length == 0 
        return true
end
directors_record_exists?(name) click to toggle source
# File lib/movie-manager-gem.rb, line 373
def directors_record_exists?(name)     
        return false if @directors_dataset.select(:id).where(:name => name).all.length == 0 
        return true
end
display_movie_info(title) click to toggle source

List and Play Movies ###

# File lib/movie-manager-gem.rb, line 106
def display_movie_info(title)
                movie_list = @movie_genre_join.where(Sequel.ilike(:title, '%'+title+'%'), :available => 1).all  # sqlite3 implementation
                if movie_list.empty?
                        puts "Sorry, couldn't find '#{title}'"
                else
                        puts "Title: #{movie_list[0][:title]}"
                        puts "Genre(s): #{movie_list.inject([]){|out, movie| out<<movie[:genre]}.join(", ")}"
                        puts "Rating: #{movie_list[0][:imdb_score]}/10"
                        puts "Length: #{movie_list[0][:length]} min."
                        puts "Summary:"
                        puts "#{movie_list[0][:summary]}"
                end
end
drop_table(table_name = table_name.to_sym) click to toggle source
# File lib/movie-manager-gem.rb, line 505
def drop_table(table_name = table_name.to_sym)
        if @@DB.table_exists?(table_name)
                puts "Are you sure you want to drop table \'#{table_name}\'? Y/N"
                response = 1000.chomp.downcase

                case response
                when 'y', 'yes'
                        @@DB.drop_table(table_name)
                        puts 'Table dropped'
                when 'n', 'no'
                        puts 'Table not dropped'
                else
                        puts 'Reply with Y or N'
                        drop_table(table_name)
                end
        else
                raise StandardError, 'Table doesn\'t exist'
        end
end
enqueue_local_movies() click to toggle source

Movie-handling fuctions ###

# File lib/movie-manager-gem.rb, line 259
def enqueue_local_movies      # works
        movies_glob = Dir.glob('**/*.{mkv,MKV,avi,AVI,mp4,MP4,mpg,MPG,mov,MOV}').uniq
        movies_glob.each {|movie| @local_movies_queue << [File.absolute_path(movie), normalize_title(movie), nil]}
        # movies.select!{|movie| File.size(movie) > 600_000_000} # works
end
find_files_in(path) click to toggle source
# File lib/movie-manager-gem.rb, line 75
def find_files_in(path) # works
        # tried using ~/ as the default path. Returned too many unrelated videos.
        Dir.chdir(path) do
                enqueue_local_movies
                add_all_movies_to_table
                update_directories_status
        end
end
genres_record_exists?(genre) click to toggle source
# File lib/movie-manager-gem.rb, line 378
def genres_record_exists?(genre)
        return false if @genres_dataset.select(:id).where(:genre => genre).all.length == 0 
        return true
end
get_imdb_movie_info(clean_name) click to toggle source
# File lib/movie-manager-gem.rb, line 355
def get_imdb_movie_info(clean_name) # works
        i = Imdb::Search.new(clean_name)
        return i.movies.first        if i.movies.first.class == Imdb::Movie
        return nil
end
initialized?() click to toggle source

Update and Refine Data ###

# File lib/movie-manager-gem.rb, line 44
def initialized?
        @movies_dataset.first
end
list_all_movies(min_score=-1) click to toggle source
# File lib/movie-manager-gem.rb, line 120
def list_all_movies(min_score=-1) # works
        movie_list = @movie_director_join.where({:available => 1}, (Sequel.expr(:imdb_score) >= min_score)).group(:movies__id).order(:title).all # sqlite3 impementation
        # movie_list = @movie_directories_join.order(:title).distinct(:movies__id,:title).where({:available => 1}, (Sequel.expr(:imdb_score) >= min_score)).all? # postgres implementation

        if movie_list.empty?
                puts "Sorry, we don\'t have any movies with a score higher than #{min_score}"
        else
                puts "--- Movies Rated Higher Than #{min_score} ---"
                movie_list.each{ |movie| puts movie[:title]} 
        end
end
list_movies_by_genre(genre, min_score = -1) click to toggle source
# File lib/movie-manager-gem.rb, line 195
def list_movies_by_genre(genre, min_score = -1) # works
        movie_list = @movie_genre_join.where(Sequel.ilike(:genre, '%'+genre+'%') & {:available => 1}).group(:movies__id).order(:title).all  # sqlite3 implementation  & (Sequel.expr(:imdb_score) >= min_score)
        # movie_list = @movie_genre_join.order(:title).distinct(:movies__id,:title).where(Sequel.ilike(:genre, '%'+genre+'%') & {:available => 1} & (Sequel.expr(:imdb_score) >= min_score)).all # postgres implementation

        if movie_list.empty?
                puts 'Sorry, we don\'t have that genre. Please enter one from the list:'
                @genres_dataset.select(:genre).order(:genre).all.each {|genre| puts '- ' + genre[:genre]}
        elsif movie_list.select {|movie| movie[:imdb_score] >= min_score}.empty?
                puts "Sorry, couldn't find any movies with genre #{movie_list[0][:genre]} and a rating above #{min_score}"
        else
                search_genre = movie_list.first[:genre]
                print "--- Movies with genre '#{search_genre}' "
                print "rated above #{min_score} " if min_score != -1
                puts "---"
                movie_list.select {|movie| movie[:imdb_score] >= min_score}.each{ |movie| puts movie[:title]} 
        end
end
list_movies_with_actor(actor, min_score = -1) click to toggle source
# File lib/movie-manager-gem.rb, line 163
def list_movies_with_actor(actor, min_score = -1) # works
        results_list = @actors_dataset.where(Sequel.ilike(:name, '%'+actor+'%')).all

        if results_list.length > 1
                temp = []
                results_list.each {|result| temp << result[:name]}
                puts "Here are the results for '#{actor}'. Please enter the number of the one you want."
                temp.each_with_index do |name,i| 
                        puts "#{i+1}: #{name}"
                end
                print '> '
                actor_name = temp[$stdin.gets.chomp.to_i - 1]

        elsif results_list.length == 1
                actor_name = results_list[0][:name]

        else
                puts "Sorry, we couldn't find an actor named '#{actor}'. Here are the actors we have:"
                @actors_dataset.select(:name).order(:name).all.each {|actor| puts '- ' + actor[:name]}
                return
        end
        movie_list = @movie_actor_join.where({:name => actor_name}, {:available => 1}, (Sequel.expr(:imdb_score) >= min_score)).group(:movies__id).order(:title).all  # sqlite3 implementation
        # movie_list = @movie_actor_join.order(:title).distinct(:movies__id,:title).where({:name => actor_name}, {:available => 1}, (Sequel.expr(:imdb_score) >= min_score)).all  # postgres implementation

        if movie_list.empty?
                puts "Sorry, we don\'t have any movies starring \'#{actor_name}\'."
        else
                puts "--- Movies starring '#{actor_name}' ---"
                movie_list.each{ |movie| puts movie[:title]}        
        end
end
list_movies_with_director(director, min_score = -1) click to toggle source
# File lib/movie-manager-gem.rb, line 132
def list_movies_with_director(director, min_score = -1) # works
        results_list = @directors_dataset.where(Sequel.ilike(:name, '%'+director+'%')).all
        if results_list.length > 1
                temp = []
                results_list.each {|result| temp << result[:name]}
                puts "Here are the results for '#{director}'. Please enter the number of the one you want."
                temp.each_with_index do |name,i| 
                        puts "#{i+1}: #{name}"
                end
                print '> '
                director_name = temp[$stdin.gets.chomp.to_i - 1]

        elsif results_list.length == 1
                director_name = results_list[0][:name]

        else
                puts "Sorry, we couldn't find a director named '#{director}'. Here are the directors we have:"
                @directors_dataset.select(:name).order(:name).all.each {|director| puts '- ' + director[:name]}
                return
        end
        movie_list = @movie_director_join.where({:name => director_name}, {:available => 1},  (Sequel.expr(:imdb_score) >= min_score)).group(:movies__id).order(:title).all  # sqlite3 implementation
        #movie_list = @movie_director_join.order(:title).distinct(:movies__id,:title).where({:name => director_name} & {:available => 1} & (Sequel.expr(:imdb_score) >= min_score)).all  # postgres implementation

        if movie_list.empty?
                puts "Sorry, we don\'t have any movies directed by \'#{director_name}\'."
        else
                puts "--- Movies directed by '#{director_name}' ---"
                movie_list.each{ |movie| puts movie[:title]}        
        end
end
movies_record_exists?(original) click to toggle source

Exists? ###

# File lib/movie-manager-gem.rb, line 363
def movies_record_exists?(original)    
        return false if @movies_dataset.select(:id).where(:original_title => original).all.length == 0 
        return true
end
normalize_title(title) click to toggle source
# File lib/movie-manager-gem.rb, line 265
def normalize_title(title) # works
        # output should seperate path, suffix. Change . and _ to spaces. Replace / and : with - to be unix-safe. remove anything in brackets or braces.
        File.basename(title,'.*').gsub(/[\.|\_]/," ").gsub(/[\/|:]/,"-").gsub(/[\[|\{].*[\]|\}]/, "")
end
play(movie_title, user_input=true) click to toggle source
# File lib/movie-manager-gem.rb, line 230
def play(movie_title, user_input=true) # works
        # does a search for the title if a user input it. opens the exact file if another function provided the name
        if user_input
                movie_to_play = @movie_directories_join.where(Sequel.ilike(:title, '%'+movie_title+'%'), :available => 1).group(:movies__id).first  # sqlite3 implementation
                # movie_to_play = @movie_directories_join.order(:title).distinct(:movies__id,:title).where(Sequel.ilike(:title, '%'+movie_title+'%'), :available => 1).first  # postgres implementation
        else
                movie_to_play = @movies_dataset.where(:title => movie_title).first
        end

        if movie_to_play 
                puts "Play #{movie_to_play[:title]}? Y/N"
                print '> '
                response = $stdin.gets.chomp.downcase
                case response
                when 'y', 'yes'
                        update_watched_status(movie_to_play[:title],1)
                        system("open \"#{movie_to_play[:original_title]}\"")       # unix needs double quotes around file names with spaces
                when 'n', 'no'
                        puts 'Okay...'
                else
                        puts 'Speak English, man!'
                        play(movie_title)
                end
        else
                puts "No movie named '#{movie_title}' found, try a different search"
        end
end
play_unseen_genre(genre, min_score = -1) click to toggle source
# File lib/movie-manager-gem.rb, line 213
def play_unseen_genre(genre, min_score = -1) # works
        movie_genre_list = @movie_genre_join.where(Sequel.ilike(:genre, '%'+genre+'%') & {:available => 1} & (Sequel.expr(:imdb_score) >= min_score)).group(:movies__id).all  # sqlite3 implementation
        # movie_genre_list = @movie_genre_join.order(:title).distinct(:movies__id,:title).where(Sequel.ilike(:genre, '%'+genre+'%') & {:available => 1} & (Sequel.expr(:imdb_score) >= min_score)).all  # postgres implementation
        if movie_genre_list.empty?
                puts "Sorry, we don\'t have any movies with the genre #{genre}."
                # this exception doesn't reveal if there are no movies of that genre above the minimum score
        else
                unwatched_list = movie_genre_list.select{|movie| movie[:watched] != 1}
                if unwatched_list.empty?
                        puts "Sorry, you\'ve seen all of your #{genre} movies"
                else
                        movie_title = unwatched_list.sample[:title]
                        play(movie_title,false)
                end
        end          
end
update_directories_status() click to toggle source
# File lib/movie-manager-gem.rb, line 270
def update_directories_status # works
        @directories_dataset.select(:directory_path).all.each do |dir|
                if Dir.exists?(dir[:directory_path])
                        @directories_dataset.where(:directory_path => dir[:directory_path]).update(:available => 1)
                else
                        @directories_dataset.where(:directory_path => dir[:directory_path]).update(:available => 0)
                end
        end
end
update_file_names() click to toggle source
# File lib/movie-manager-gem.rb, line 48
def update_file_names
  incorrect_names = @movies_dataset.select(:id, :original_title, :title).where(:correct_filename => 0).all
  incorrect_names.each do |name|
      puts "Is \"#{name[:title]}\" the correct title for \"#{File.basename(name[:original_title],".*")}\"? \n y/n"
      print '> '
      response = $stdin.gets.chomp.downcase
                case response
                when 'y','yes'
                        new_title = File.dirname(name[:original_title]) + '/' + name[:title].gsub(/[\.|\_]/," ").gsub(/[\/|:]/,"-") + File.extname(name[:original_title])
                        File.rename(name[:original_title], new_title) #this is also in normalize
                        # mac replaces ':' with '/', this is a problem
                        # directories get confused when a movie title has a '/' in it
                        @movies_dataset.where(:id => name[:id]).update(:correct_filename => 1, :original_title => new_title)
                        puts "File updated"
                when 'n', 'no'
                        # incorrect title, must refine, replace our RT search records
                        @movies_dataset.where(:id => name[:id]).update(:correct_filename => -1)
                        puts "crap, sorry..."
                when 'end','exit'
                        break
                else
                        puts "Respond with 'y' or 'n'"
                        # you'll have to run the command again to continue
                end
        end
end
update_watched_list() click to toggle source
# File lib/movie-manager-gem.rb, line 84
def update_watched_list # works
        @movies_dataset.where(:watched => -1).all.each do |movie|
                puts "Have you seen #{movie[:title]}? Y/N or end"
                print '> '
                response = $stdin.gets.chomp.downcase
                case response
                when 'y', 'yes'
                        update_watched_status(movie[:title],1)
                        puts 'Record updated'
                when 'n', 'no'
                        update_watched_status(movie[:title],0)
                        puts 'Record updated'
                when 'end'
                        break
                else
                        puts 'I didn\'t catch that..'
                        update_watched_list
                end
        end
end
update_watched_status(movie_title, status) click to toggle source
# File lib/movie-manager-gem.rb, line 280
def update_watched_status(movie_title, status)        # works.
        @movies_dataset.where(:title => movie_title).update(:watched => status)
end