module Movier # Organize a given folder of movies # # * *Args* : # - +path+ -> path to the folder with movies that need to be organized # def self.organize(path, options = {}) # path where files that need to be organized are. path = path.first path = Dir.pwd if not path or path.empty? path = File.expand_path(path) # select files that are larger than 100 MB and not yet, organized. movies = Dir.glob("#{path}/**/*") movies = movies.select{ |f| File.size(f) >= 100*2**20 } movies = movies.reject{ |f| organized?(f) } # if we can't find such files, let the user know so. # otherwise, display how many files, we have found if movies.count > 0 tip_now("Found approx. #{movies.count} movies") # ask user where the organized files will be put up? tip_now "Movies will be saved as: <organized>/<Language>/<rating>+/<movie_name> [<year>]" options[:dir] = ask_for_directory("Where should I put the organized files? ") # organize movies one by one movies.each { |movie_path| organize_movie movie_path, options } # show that we are done passed_with("All movies were organized!") else passed_with("All movies have already been organized.") end end private # Organize a single movie, given a path # # * *Args* : # - +movie_path+ -> path of the movie that will be organized # def self.organize_movie(movie_path, options) movie_path = File.expand_path(movie_path) # let the user know which movie we are parsing.. tip_now movie_path, "Checking.." # pick a target movie from IMDB.com based on the file path movie = pick_movie movie_path, options[:guess] # if we were unable to find the movie, or it was ignored, move ahead return unless movie # let the user know, which target was selected imdb = movie[:imdb] selected = "#{imdb["Title"]} (#{imdb["Year"]})" selected += " at #{imdb["imdbRating"]} points with #{imdb["imdbVotes"]} votes" say_rated "Selected", selected, imdb["imdbRating"] # since, we can't find movie's language, ask user for it. lang = options[:lang] || ask("What language this movie is in? ") {|x| x.default = "en" } # rearrange/reorganize the movie set_language_and_rearrange(movie, options[:dir], lang) end # pick a target movie from IMDB.com based on the movie's file path # # * *Args* : # - +movie+ -> path to the movie # * *Returns* : # - hash of information about the selected movie target # def self.pick_movie(movie_path, guess = true) # pick -> our selected target from IMDB.com pick = false # get whatever information we can get from movie's path movie = sanitize_and_get_information_from movie_path # search for movies with this information search = search_movie(movie[:name], movie[:year]) # get information from IMDB.com for each movie found search.map!{ |mov| fetch_details mov["imdbID"] } # sort our movies search.sort_by {|m| m["weight"] } # can we make an intelligent guess ? # puts search.inspect guessable = guess && search.count > 1 guessable = ( search[1]["votes"] < 5000 && search[0]["votes"] > 100000 ) || ( search[0]['votes'] > 10000 && search[0]["weight"] > 40 * search[1]["weight"] ) if guessable # pick if only one movie was found, or if search is guessable pick = search[0] if search.count == 1 || guessable # let the user pick a movie otherwise from given options unless pick tip_now "Please, choose the correct movie title below:" choose do |menu| # let the user pick from found movie titles search.each do |m| m["details"] = "#{m["Title"]} [#{m["Year"]}] at " m["details"] += "#{m["imdbRating"]} points " m["details"] += "with #{m["imdbVotes"]} votes.\n\t" m["details"] += "Plot: #{m["Plot"]}\n\t" m["details"] += "Actors: #{m["Actors"]}\n" menu.choice(m["details"]) { pick = m } end # let the user provide an IMDB ID menu.choice("[ Use IMDB ID ]") do id = ask("IMDB.com ID for the given title? ") pick = fetch_details id end menu.choice("[ Use another keyword ]") do keyword = ask("Keyword to search with? ") pick_movie keyword end # let the user ignore this movie for organizing purposes menu.choice("[ Ignore ]") { return false } end end # return our pick or false for skipping this movie movie[:imdb] = pick pick ? movie : false end # Search a movie given some keywords, and optionally, an year. # Loop until we have a target movie from IMDB.com. # If need arises, ask user to provide us some keywords or an IMDB.com ID. # # * *Args* : # - +keyword+ -> keywords for the search # - +year+ -> (optional) year of this movie # - +ask_user+ -> if true, asks a user for movie name/id # * *Returns* : # - hash of search results # def self.search_movie(keyword, year = nil, ask_user = false) search = []; counter = 1 until search.any? # on first search, it will search with keyword being passed # on second search, will search with initial two words in the keyword # on third search and onwards, will ask user for keywords/id tip_now "Using keywords: #{keyword}", "Searching.." if counter > 1 begin; search = search(keyword, year, "movie"); rescue; end begin; search = search(keyword, nil, "Movie") if year and not search.any?; rescue; end if counter == 1 keyword = keyword.split keyword = keyword.count == 1 ? keyword[0] : "#{keyword[0]} #{keyword[1]}" elsif not search.any? keyword = ask("Please, provide keywords/IMDB ID to search with: ") return [{ "imdbID" => keyword }] if keyword =~ /tt\d{7}/ end counter += 1; end search end # Set language for a movie, and then rearrange/reorganize it on the disk # # * *Args* : # - +movie+ -> hash of information about this movie # - +path+ -> directory where the organized movies will be kept # - +lang+ -> language of this movie # def self.set_language_and_rearrange(movie, org_path, lang = "en") # get the language for this movie lang = case lang.downcase when "en", "eng", "english" then "English" when "hi", "hindi" then "Hindi" when "fr", "french" then "French" else lang.titlecase end # set the directory path for this movie # directory structure is: # <org_path>/<language>/<rating>+/<movie name> [<year>]/ # e.g. for the Top Gun movie: # /Volumes/JukeBox/Movies/English/6+/Top Gun [1986]/ imdb = movie[:imdb] nice_name = "#{imdb["Title"]} (#{imdb["Year"]})".gsub(/(\\|\/|\:)/, ' - ') movie_path = File.join(org_path, lang, "#{imdb["imdbRating"].to_i}+", nice_name) FileUtils.mkdir_p movie_path Dir.chdir(movie_path) do # move the movie to this folder # TODO: skip copying the file if this movie already exists FileUtils.mv movie[:path], nice_name + File.extname(movie[:path]) # create imdb.txt file inside this folder File.open("imdb.txt", "w") {|f| f.puts movie.to_yaml} # download the movie posted to this folder `wget #{imdb["Poster"]} -qO poster#{File.extname(imdb["Poster"])}` unless imdb["Poster"] == "N/A" end end # Sanitize whatever information we got from movie's path # # * *Args* : # - +path+ -> path to the movie # * *Returns* : # - hash of sanitized information about the movie # def self.sanitize_and_get_information_from(path) movie = capture_movie(path) movie[:name] = movie[:name].gsub(/(BR|DVD|[a-z]*)Rip/i, "") movie[:name] = movie[:name].gsub(/divx/i, "") movie[:name] = movie[:name].gsub(/[._-]/, " ").strip movie[:parent] = File.basename(File.dirname(path)) movie[:path] = path movie end # Capture information about the movie from its path # # * *Args* : # - +path+ -> path of this movie # * *Returns* : # - hash of information about the movie # def self.capture_movie(path) movie = File.basename(path, File.extname(path)) # try for: movie name [year] something.something regex = /(.*)\[(\d{4})\].*/ match = movie.match regex return {type: 1, name: match[1], year: match[2]} if match # try for: movie name (year) something.something regex = /(.*)\((\d{4})\).*/ match = movie.match regex return {type: 2, name: match[1], year: match[2]} if match # try for: movie name year something.something regex = /(.*)(\d{4}).*/ match = movie.match regex return {type: 3, name: match[1], year: match[2]} if match # go generic return {type: 0, name: movie } end end