class Renamer

Public Class Methods

new(args) click to toggle source
# File lib/tv_renamer/renamer.rb, line 145
def initialize(args)
  @output_dir = '.'
  @rename = true
  i=0
  while i < args.size
    case args[i]
      when "-i"
        @ini_file = args[i+1]
        if @ini_file.nil?
          puts "You must enter the path to the shows.ini file"
          exit
        end
        i += 1
      when "--output-dir", "-d"
        @output_dir = args[i+1]
        if @output_dir.nil?
          puts "You must enter a directory to renamer files to!"
          exit 1
        end
        i += 1
      when "--debug"
        @debug = true
      when "--no-rename", "-n"
        @rename = false
      when "--overwrite", "-o"
        @overwrite = true
      when "--verbose", "-v"
        @verbose = true
    end
    i += 1
  end

  if(!@ini_file)
    #check if windows or linux
    if RUBY_PLATFORM['linux']
      if ENV['XDG_CONFIG_HOME']
        basedir = ENV['XDG_CONFIG_HOME']
      else
        if ENV['HOME']
          basedir = File.join(ENV['HOME'], '.config')
        else
          puts '$XDG_CONFIG_HOME and $HOME unset, falling back to current directory'
          basedir = '.'
        end
      end
    else
      basedir = ENV['HOMEDRIVE'] + ENV['HOMEPATH']
    end
    
    @ini_file = File.join(basedir, 'shows.ini')
  end

  begin
    @ini = Ini.new(@ini_file, true)
  rescue
    puts "#{@ini_file} does not exist, no custom renaming available"
  end
end

Public Instance Methods

attribute(attribute, show) click to toggle source
# File lib/tv_renamer/renamer.rb, line 393
def attribute(attribute, show)
  if show
    attr = @ini[show.downcase][attribute] unless @ini.nil? or @ini[show.downcase].nil?
    if attr
      return attr
    else
      return attribute(attribute, nil)
    end
  else
    return @ini[attribute] unless @ini.nil?
  end
end
epguide_data(video, url) click to toggle source
# File lib/tv_renamer/renamer.rb, line 352
def epguide_data(video, url)
  if !File::exist?(url + ".renamer")
    page = Net::HTTP.new('www.epguides.com')

    response = page.get("/#{url}/")

    case response
    when Net::HTTPSuccess
      data = response.body
      
      File.open(url + ".renamer", "w") do |file|
        file << data
      end
    else
      if !@ini.nil?
        puts "#{@ini_file} does not exist, please create this file and add an alias for #{video.show}"
        return nil
      end
      
      unless @ini[video.show.downcase]
        puts "Please add an alias of the epguide.com show url for \"#{video.show}\" to #{@ini_file}"
        return nil
      end
      
      if @ini[video.show.downcase]["url"]
        puts "The entry for \"#{video.show}\" has an invalid URL"
      else
        puts "The entry for \"#{video.show}\" needs a URL"
      end

      data = nil
    end
  else
    File.open(url + ".renamer", "r") do |file|
      data = file.read
    end
  end

  return data
end
epguide_line(video) click to toggle source

returns the line of html from epguides.com that contains the information for this episode

# File lib/tv_renamer/renamer.rb, line 423
def epguide_line(video)
  url = show_url(video)
  data = epguide_data(video, url)

  return nil if data.nil?

  # default to TV.com pages
  pattern = matchstring(video, false)

  # get each line
  lines =  data.split(/\n|\r/)

  # go through each until we find the show data
  lines.each do |line|
    if line =~ /((_+) )+/
      video.format = line
    elsif line.match(pattern)
      return line
    elsif line.match("this TVRage editor")
      pattern = matchstring(video, true)
    end
  end

  puts "Epguides does not have #{video.show} season: #{video.season} episode: #{video.episode_number} in its guides."
  nil
end
generate_filename(video) click to toggle source
# File lib/tv_renamer/renamer.rb, line 306
def generate_filename(video)
  # if the ini file exists, and they set a custom renaming mask,
  # or a global one exists we need to do more custom renaming
  if mask = attribute('mask', video.orig_show)
    # set the filename to the mask as a base

    filename = mask.dup

    # substitute the patterns with the data we found
    filename.gsub!("%show%", video.show)
    filename.gsub!("%episode%", video.episode_name)
    filename.gsub!("%season%", video.season)
    filename.gsub!("%epnumber%", video.episode_number)

    # if there is a custom date format, use that
    # otherwise date is however epguides displays it
    if !video.date.nil? && date_format = attribute('dateformat', video.orig_show)
      video.date.insert(-3, "20")
      video.date = Date.parse(video.date).strftime(date_format)
    end

    # TODO: we need to handle this better if date, code, etc.. don't exists
    # right now they just end up as spaces
    filename.gsub!("%date%", video.date)
    filename.gsub!("%code%", video.production_code)
  # if we don't have a shows.ini, or nothing specific is set, use the default pattern
  else
    filename = [video.show, video.season, video.episode_number,  video.episode_name].join(' - ')
  end

  # add on the extension
  filename = "#{filename}.#{video.extension}"

  # replace html encoded characters
  filename = CGI.unescapeHTML(filename)

  # replace these illegal win32 characters with '-'
  filename.gsub!(":", "-")
  filename.gsub!("/", "-")

  # just delete theses illegal win32 characters
  filename.delete!("?\\/<>\"")

  filename
end
matchstring(video, tvrage) click to toggle source
# File lib/tv_renamer/renamer.rb, line 406
def matchstring(video, tvrage)
  if video.rename_by_date && video.date
    if !tvrage
      video.date
    else
      video.date.gsub(' ', '/')
    end
  else
    if !tvrage
      matchstring = "#{video.season}-#{sprintf('%2s', video.episode_number)}"
    else
      matchstring = "#{video.season}-#{sprintf('%02i', video.episode_number.to_i)}"
    end
  end
end
parse_line(line, format) click to toggle source
# File lib/tv_renamer/renamer.rb, line 450
def parse_line(line, format)
  if format.nil?
    if line.match("<li>")
      format = "____ _______ ________ ___________ ______________________________________________"
    else
      format = "_____ ______ ___________  ___________ ___________________________________________"
    end

    puts "Couldn't find data format string from epguides page, assuming format like:"
    puts format
  end

  # ensure format ends in exactly one space so loop gets all tokens
  format = format.split(' ').join(' ') + ' '

  tokens = []
  prev_offset = -1
  while offset = format.index(' ', prev_offset + 1) do
    range = (prev_offset+1)..(offset - 1)
    tokens.push line.slice(range).strip
    prev_offset = offset
  end

  return tokens
end
rename(video) click to toggle source
# File lib/tv_renamer/renamer.rb, line 266
def rename(video)
  return false unless set_attributes_from_epguides(video)

  # if the ini exists, we set the showname to the custome name if it exists
  if customname = attribute('customname', video.orig_show)
    video.show = customname
  end

  # pad the episode with 0 if length is less than 0, we need to handle
  # this better for 3+ digit episodes seasons
  video.episode_number = sprintf("%02i", video.episode_number.to_i)

  return false unless new_filename = generate_filename(video)

  new_filename = [@output_dir, new_filename].join(File::Separator)

  if !@rename or @verbose
    puts "rename #{video.filename} to #{new_filename}"
  end

  if @rename
    # if the file doesn't exist (or we allow overwrites), we rename it
    if !File::exist?(new_filename) or @overwrite
      begin
        FileUtils.mv(video.filename, new_filename)
        @one_rename_succeeded = true
        puts "rename succeeded"
      rescue Exception
        @one_rename_failed = true
        puts "rename failed"
      end
    # otherwise we don't overwrite it and just display a message
    else
      puts "Can't rename #{video.filename} to #{new_filename}!"
      puts "#{video.filename} already exists!"
      @one_rename_failed = true
    end
  end
end
run() click to toggle source
# File lib/tv_renamer/renamer.rb, line 204
def run
  files = Dir['*.{avi,wmv,divx,mpg,mpeg,xvid,mp4,mkv}'].sort

  files.each do |file|
    video = VideoFile.new(file)

    video.rename_by_date = true if video.show && attribute('renamebydate', video.orig_show)

    # parse the show into @show, @season, @episode_number, etc...
    if !video.parsed_ok?
      puts "I could not match #{file} to a naming pattern I can interpret"
      next
    end

    rename(video)
  end

  # delete the cached results from epguides
  Dir['*.renamer'].each do |filename|
    File::delete(filename)
  end

  if @one_rename_failed
    # if some renames succeeded, return 2
    if @one_rename_succeeded
      exit 2
    # if no renames succeeded, return 1
    else
      exit 1
    end
  end
end
set_attributes_from_epguides(video) click to toggle source
# File lib/tv_renamer/renamer.rb, line 237
def set_attributes_from_epguides(video)
  line = epguide_line(video)
  return false if line.nil?

  info = parse_line(line, video.format)
  return false if info.nil?

  season_episode = info[1]
  video.production_code = info[2]
  video.date = info[3]
  episode_name = info[4]

  if line.match("<li>")
    video.episode_name = episode_name
  else
    doc = Nokogiri::HTML("<pre>#{line}</pre>")
    links = doc.css('pre a')
    if links.empty?
      puts "Could not find episode name for #{video}"
    else
      video.episode_name = links[0].content
    end
  end

  seasonmatch = season_episode.match(/(\d\d?)- ?(\d+)/)
  video.season ||= seasonmatch[1]
  video.episode_number ||= seasonmatch[2]
end
show_url(video) click to toggle source
# File lib/tv_renamer/renamer.rb, line 476
def show_url(video)
  # use the url specified in the ini, if set
  url = attribute('url', video.show)

  if url.nil?
    url = video.show.split(' ').join

    url = url[3, url.length] if url[0,3] == "The"
  end

  url
end