require "open3" require "fileutils" require "shellwords" require_relative "../ebook_renamer" module EbookRenamer EbookMetaNotInstall = Class.new(StandardError) # Extract meta data from the input file using the ebook-meta tool # # @param [String] filename the input file name # @param [String] binary the executable for use to extract the metadata # @return [String] result of the output from running the command def meta(filename, binary = "ebook-meta") fail EbookMetaNotInstall, "Need to install ebook-meta to use this gem" if AgileUtils::Helper.which(binary).nil? command = [ binary, Shellwords.escape(filename) ] stdout_str, stderr_str, status = Open3.capture3(command.join(" ")) fail "Problem processing #{filename}" unless status.success? stdout_str end # Convert the output string to hash # # @param [String] text output string from the 'ebook-meta' command # @return [Hash] hash pair for the input string def meta_to_hash(text) hash = {} return hash if text.nil? result_list = [] text.split(/\n/).each do |meta| # split by the first ':' string list = meta.split /^(.*?):/ # ignore the empty string element list.delete_at(0) unless list.empty? list.map(&:strip!) # downcase the first item to make it easy result_list << [list[0].downcase, list[1]] hash = Hash[*result_list.flatten] end end hash end # Clean the filename to remove the special characters # # @param [String] filename input file # @param [String] sep_char separator character to use # # @return [String] the new file name with special characters replaced or removed. def sanitize_filename(filename, sep_char = nil) dot = "." # Note exclude the '.' (dot) filename.gsub!(/[^0-9A-Za-z\-_ ]/, dot) # replace multiple occurrences of a given char with a dot ["-", "_", " "].each do |c| filename.gsub!(/#{Regexp.quote(c)}+/, dot) end # replace multiple occurrence of dot with one dot filename.gsub!(/#{Regexp.quote(dot)}+/, dot) return filename.gsub!(/#{Regexp.quote(dot)}+/, sep_char) if sep_char filename.strip end # Return formatted file name using the metadata values # # @param [Hash] meta_hash output from the program 'ebook-meta' or 'exiftoo' # @param [Hash] fields list of fields that will be used to set the name def formatted_name(meta_hash = {}, fields = {}) if hash.nil? || fields.nil? fail ArgumentError.new("Argument must not be nil") end # The keys that we get from the 'mdls' or 'exiftool' args = { keys: [ "title", "author(s)" ], sep_char: " " }.merge(fields) keys = args[:keys] sep_char = args[:sep_char] # Note: only show if we have the value for title result = [] if meta_hash.fetch("title", nil) keys.each do |k| value = meta_hash.fetch(k, nil) # Note: don't add 'Author(s)' => 'Unknown' to keep the result clean if value && value.downcase != "unknown" result << meta_hash[k] end end return result.join(sep_char) end # Note: if no title we choose to return empty value for result "" end # Ensure that the values in hash are sanitized # # @param [Hash] hash input hash to be sanitized # @return [Hash] original hash with values sanitized # @see #sanitize_filename def sanitize_values(hash = {}) hash.each do |key, value| hash[key] = sanitize_filename(value, " ") end hash end # List files base on given options # options: # :recursive - process the directory recursively (default false) # :exts - list of extensions to be search (default ['epub','mobi','pdf']) # # @param base_dir [String] the starting directory # @param options [Hash] the options to be used # @return [List] list of matching files or empty list if none are found def files(base_dir = Dir.pwd, options = {}) args = { recursive: false, exts: %w[epub mobi pdf] }.merge(options) wildcard = args[:recursive] ? "**" : "" patterns = File.join(base_dir, wildcard, "*.{#{(args[:exts]).join(",")}}") Dir.glob(patterns) || [] end end