Amazon_shop2ver='$Id: amazon_shop2.rb,v 1.4 2006/10/01 13:26:25 waka Exp waka $'.split(" ")[2]
=begin
= amazon_shop2 - nDiary フィルタ。amazon から各種情報をゲットし、日記内に表示する

 Authors:: waka
 Version:: '$Revision: 1.4 $'
 License:: Ruby ライセンスに準拠
== 使い方
 日記中に
 (@@DVD:BCBA-0618)
 (@@CD:AMCY-7125)
 みたいな
 (@@DVD:背表紙にある記号)
 (@@CD:背表紙にある記号)
 という形式の文字列があると、amazon(http://www.amazon.co.jp/) から
 書誌、DVD,CDの情報やら書影、ジャケット画像、商品画像やらを引っ張ってきて置き換えます。

 ※ フィルタ認識文字 '@@' は、設定で変更できます(後述「その他の設定」参照)

== 対応商品
 一部主要な商品情報取得に Amazon Web Service を利用していますので、
 www.amazon.co.jp が取り扱っているすべての商品に対応しています。(たぶん)
 ※ @@ に続くキーワードは設定で追加できます(後述「その他の設定」参照)

 - CD
 -- (@@CD:背表紙記号もしくは ASIN)

 - DVD
 -- (@@DVD:背表紙記号もしくは ASIN)

 - PlayStation
 -- (@@PS:背表紙記号もしくは ASIN)

 - PlayStation2
 -- (@@PS2:背表紙記号もしくは ASIN)

 - PSP(PlayStationPortable)
 -- (@@PSP:背表紙記号もしくは ASIN)

 - GameCube
 -- (@@GC:背表紙記号もしくは ASIN)

 - Game Boy Advance
 -- (@@GBA:背表紙記号もしくは ASIN)

 - Nintendo DS
 -- (@@NDS:背表紙記号もしくは ASIN)

 - XBOX
 -- (@@XBOX:背表紙記号もしくは ASIN)

 - 本
 -- (@@ISBN:ISBN)
 -- (@@JAN:JAN コード)

 - 全商品
 -- (@@ASIN:ASIN)

== 仕様
 ruby 1.8.x 以上でのみ動作します。

== 商品情報取得方法の選択
 商品情報取得に使用している、AmazonWebService 利用方法の選択ができます

 ・デフォルト
 http://www.double-red.net/cgi-bin/aws/
 という個人のサイトから Amazon の商品情報を取得します。

 ・ローカル PC で自分のデベロッパートークンを使用
 個人サイトからの情報取得を避ける場合、
 別途 aws4as2 をインストールし、設定する事でローカルで取得ができます。

 (1)デベロッパートークンを Amazon から取得(取得方法はご自身で調べてください)
 (2)アソシエイト ID を取得(取得方法はご自身で調べてください)
 (3)ruby-amazon をゲット
     http://raa.ruby-lang.org/project/ruby-amazon/
 (3)aws4as2.rb をゲット
     http://www.double-red.net/software/aws4as2.rb
 (4)ndiary.conf に以下を追記
   USER_LIB_DIRECTORY = '/dokoka/ndiary/lib'
   USE_AWS4AS2 = 'true'
   AMAZON_DEV_TOKEN = 'デベロッパートークン'
       (BOOKSTORE_AMAZON_TOKEN = 'デベロッパートークン' でも OK)
   AMAZON_ASSOCIATE_ID = 'アソシエイト ID セット'
       (BOOKSTORE_AMAZON_ID = 'アソシエイト ID' でも OK)
 (5)ruby-amazon をインストール
  ※インストール方法がわからない場合
    ruby-amazon-x.x.x.tar.gz を展開し、
    USER_LIB_DIRECTORY 内に ruby-amazon というディレクトリ作って
    その中へ一式(lib ディレクトリを含めば OK)を入れてください。
 (6)aws4as2.rb を USER_LIB_DIRECTORY に入れる
 (例)
USER_LIB_DIRECTORY = '/ndiary/lib'
USE_AWS4AS2 = 'true'
AMAZON_DEV_TOKEN = 'HOGEHOGE'
AMAZON_ASSOCIATE_ID = 'FUGAFUGA'



== キャッシュディレクトリについて
 ndiary.conf の LOG_DIRECTORY で指定したディレクトリに
 amazon_shop2
 というディレクトリを「勝手に」生成して、取得したデータをキャッシュします

== amazondvd.rb、amazon_shop.rb を利用していた方へ

 キャッシュの上位互換はありません。
 ただし、それぞれのキャッシュディレクトリと書式が異なるので、衝突はありません。
 同時使用で構いません。

 設定については同一です。
 特に変更するような追加設定項目は今のところありません。
 書式は異なります。過去の日記への amazon_shop2 を適用するためには、
 日記を全て書き直すか、
 amazon_shop2 の前に一発フィルタをかまして amazon_shop2 の書式変換して運用することになります

== キャッシュ保持期間(実装予定)
  現在対応していません。この項目は読み飛ばしてください。
  キャッシュの有効期限を指定できます。
  ndiary.conf にて
  変数 AMAZON_CACHE_EXPIRE に日数(正の整数)を指定してください。
  (0は無期限)
  (例)
  AMAZON_CACHE_EXPIRE = '30'

== プロキシ設定(必須ではない)
 ndiary.conf で
 PROXY = 'proxy.hoge.com:8080'
 と指定しておくと、情報取得時プロクシを経由するようになります。

== その他の設定(必須ではない)
 ndiary.conf にて以下の変数が使用できます

 - AMAZON_SHOP2_PREFIX(amazon_shop2 の書式スタート文字変更)
 デフォルト: '@@'
 (例)
 AMAZON_SHOP2_PREFIX = '@'

 - AMAZON_CATALOG(amazon_shop2 の書式追加)
 (例)
 AMAZON_CATALOG = 'CD|DVD|PS|PS2|PSP|GC|GBA|NDS|XBOX|ASIN|ISBN|JAN|OS|SPORTS'

 - BOOKSTORE_AMAZON_ID(Amazonアソシエイトに参加している場合に有効)
 (例)
 BOOKSTORE_AMAZON_ID = 'hogehoge-foo'

 - NO_IMAGE_FILE
 ジャケット画像がない場合に代替に表示する画像(imgタグで表記)
 (例)
 NO_IMAGE_FILE='<img src="http://hogehoge.com/foo/img/noimage.png" alt="no image">'

== テンプレートにより表示をカスタマイズ
 ndiary.conf に以下を設定

 AMAZON_SHOP_TEMPLATE= true

 キャッシュディレクトリ(amazon_shop2)以下に"template.txt" という名前のテンプレートファイルを用意します。

=== テンプレートファイルを用意

テンプレートファイルの例ここから
 ※行頭 "#" で始まる行はコメント扱いで、無視(スキップ)されます。
<ul>
# 商品名
["@product_name","<li>『","』"]
# メディア(CDとか)
["@media","(<span class=\"media\">Media</span>: ",")</li>"]
# プラットフォーム(ゲーム Nintendo DSとか)
["@platform","(<span class=\"media\">Media</span>: ",")</li>"]
<ul>
# 商品画像
["@image_url_medium","<li>","</li>"]
# 会社名
["@manufacturer","<li>【","】</li>"]
# 著者(本)
["@authors","<li>【","】</li>"]
# 監督(DVDなど)
["@directors","<li>【","】</li>"]
# 出演者(DVDなど)
["@starring","<li>【","】</li>"]
<li>
# 発売日
["@release_date","【<span class=\"pubDate\">発売日</span>: ","】"]
# ページ数(本の場合)
["@book_page","【<span class=\"size\">ページ</span>: ","ページ】"]
# 値段(値引き後)
["@our_price","【<span class=\"price\">価格</span>:","】"]
</li>
#レビュワー
["@reviewer","<li>【","】</li>"]
#商品レビュー(紹介文)
["@review","<ul><li>","</li></ul>"]
#詳細スペック
["@product_detail","<li>【仕様】","</li>"]
# 再生時間
["@dvd_play_time","<li>【再生時間 ","】</li>"]
# DVD形式
["@dvd_extra_spec","<li>【形式 ","】</li>"]
# 画面サイズ
["@dvd_display_size","<li>【画面サイズ ","】</li>"]
# メディア枚数
["@num_media","<li>【","】</li>"]
# ASIN
["@asin","<li>ASIN ","</li>"]
# ISBN
["@isbn","<li>ISBN ","</li>"]
# JAN コード
["@jancode","<li>ISBN ","</li>"]
</ul>
</ul>
 テンプレートファイルの例ここまで
 ["キーワード","キーワードの前に置かれる文字列","キーワードの後に置かれる文字列"]

== Speial Thanks
 - not (のとや) さん
   -- http://www14.cds.ne.jp/~not/
   -- nDiary本体とamazon.rbの作者
   -- amazon.rb のソースに少し変更を加えたものが amazondvd.rb
           そこからさらに発展したものが amazon_shop.rb
           amazon (2006/9)仕様変更対応のため、スクラッチから書き直したのが amazon_shop2.rb
 - mas さん
   -- http://masyos.sakura.ne.jp/
   -- バグ報告や動作確認、Amazonアソシエイト対応など
 - Ace of Diamond さん
   -- http://every.pobox.ne.jp/
   -- Amazonアソシエイト対応コード不具合パッチなど
 - Tmas さん
   -- http://tmas.s54.xrea.com/diary/
   -- JAN コード対応
 - amazondvd.rb / amazon_shop.rb / amazon_shop2.rb 利用者の方々

== 注意事項
 著作権がらみの情報を取得するため、
 www.amazon.co.jpのアソシエイトプログラムを確認の上自己責任でご利用下さい。
=end
require 'net/http'
require 'cgi'
require 'timeout'
require 'nkf'
require 'yaml'
require 'fileutils'

class String
  require 'iconv'
  def myescape
    begin
      CGI::escape(Iconv.conv("UTF-8", "SHIFT_JIS", self))
    rescue
      self
    end
  end
end

# $DEBUG = true
 $DEBUG = false
class AmazonShop2
  attr_accessor :asin
  attr_accessor :isbn
  attr_accessor :jancode
  attr_accessor :book_label
  attr_accessor :catalog
  attr_accessor :cache_dir
  attr_accessor :proxy
  attr_accessor :srch_index
  attr_accessor :srch_keywd
  attr_accessor :product_name
  attr_accessor :manufacturer
  attr_accessor :authors
  attr_accessor :our_price
  attr_accessor :list_price
  attr_accessor :release_date
  attr_accessor :review
  attr_accessor :reviewer
  attr_accessor :srch_url
  attr_accessor :num_media
  attr_accessor :book_size
  attr_accessor :book_page
  attr_accessor :media
  attr_accessor :platform
  attr_accessor :platforms
  attr_accessor :image_url_small
  attr_accessor :image_url_medium
  attr_accessor :image_url_medium_wh
  attr_accessor :image_url_large
  attr_accessor :image_url_large_wh
  attr_accessor :dvd_extra_spec		#形式: Color, Widescreen, Dolby など
  attr_accessor :dvd_region_code	#リージョンコード
  attr_accessor :dvd_display_size	#画面サイズ: 1.78:1 など
  attr_accessor :dvd_play_time		# 再生時間
  attr_accessor :directors		# 監督
  attr_accessor :starring		# 出演者
  attr_accessor :mpn			#メーカー型番
  attr_accessor :product_detail	# 詳細仕様

  attr_accessor :ruby_amz_lib
  attr_accessor :use_aws4as2	# 商品情報取得に aws4as2 ライブラリを使用する
  attr_accessor :aws_token		# デベロッパートークン。use_aws4as2 true の時、必須
  attr_accessor :associate_id	# アソシエイト ID。use_aws4as2 true の時、必須

  attr_accessor :diary_day
  attr_accessor :cached_day, :retry_day

  attr_accessor :noimage_retry_span

  def initialize
    @asin = ''
    @isbn = ''
    @jancode = ''
    @book_label = ''
    @catalog = ''
    @cache_dir = ''
    @proxy = ''
    @srch_index = ''
    @srch_keywd = ''

    @product_name = ''

    @manufacturer = []
    @authors = []

    @our_price = ''
    @list_price = ''
    @release_date = ''

    @review = ''
    @reviewer = ''
    @srch_url = ''

    @num_media = ''

    @book_size = ''
    @book_page = ''
    @media = ''
    @platform = ''
    @platforms = []

    @image_url_small = ''
    @image_url_medium = ''
    @image_url_medium_wh = [0, 0, 0]
    @image_url_large = ''
    @image_url_large_wh = [0, 0, 0]
    @dvd_extra_spec = ''
    @dvd_region_code = ''
    @dvd_display_size = ''
    @dvd_play_time = ''

    @directors = []
    @starring = []
    @mpn = ''
    @product_detail = ''

    @ruby_amz_lib = ''

    @use_aws4as2 = false
    @aws_token = ''
    @associate_id = ''

    @diary_day = Time.now
    @cached_day = Time.now
    @retry_day = Time.now
    @noimage_retry_span = 0
  end

  def save_obj
    newobj = Hash.new
    self.instance_variables.each{|instance|
      newobj["#{instance}"]  = self.instance_variable_get(instance)
    }
    return newobj
  end

  def load_obj(obj)
    self.instance_variables.each{|instance|
      eval(%Q'#{instance} = obj["#{instance}"]')
    }
  end

  def save_cache(cache_file)
    begin
      obj = save_obj
      cache = File.open(cache_file.untaint, "w")
      cache.flock(File::LOCK_EX)
      cache.write(obj.to_yaml)
      cache.flock(File::LOCK_UN)
      cache.close
    rescue
      puts "error"#
    end
  end

  def load_cache(cache_file)
    begin
      cache = File.open(cache_file.untaint, "r")
      obj = YAML::load(cache)
      cache.close
    rescue
      raise "load_cache error : " + $!
    end
    load_obj(obj)
  end

  def cache_dir=(dir)
    dir << '/' unless dir[-1,1] == '/'
    @cache_dir = dir
  end

  def get_imagesize(url_img)
    image_info = [0, 0, 0]
    begin
      require 'lib/imagesize'
    rescue LoadError
      return image_info
    end
    suffix = url_img.split(".").last
    begin
      # $stderr.puts "umask: #{File::umask}" if $DEBUG
      defaultumask = File::umask(022)
      #downloadImgFile = File.join(@cache_dir,"amazon_shop2_tmp.#{suffix}")
      downloadImgFile = File.join(@cache_dir, File.basename(url_img))
      downloadImg = File.open(downloadImgFile.untaint,"w")
      downloadImg.flock(File::LOCK_EX)
      downloadImg.puts download(url_img, @proxy)
      downloadImg.flock(File::LOCK_UN)
      downloadImg.close
      w, h = ImageSize::size(downloadImgFile) # <= lib/imagesize.rb 参照。2005-6-28(Tue)
      w, h, fsize = [0, 0, 0] if w <= 1 || h <= 1
      fsize = FileTest::size(downloadImgFile)
      begin
        #File.delete(downloadImgFile) if FileTest.exist?(downloadImgFile)
        FileUtils.rm_f(downloadImgFile) if FileTest.exist?(downloadImgFile)
      rescue
        # ファイル削除できなかったけど終了する事もないだろう。
        $stderr.puts $!.backtrace if $DEBUG
      end
      image_info = [w, h, fsize]
    rescue
      $stderr.puts $!.backtrace if $DEBUG
    end
    return image_info
  end

  def get_detail(asin)
    url = %Q'http://www.amazon.co.jp/gp/product/product-details/#{asin}/'
    begin
      request_body = download(url).to_s
      return if request_body.to_s.empty?
    rescue
      return
    end
    product_detail_scope = request_body.dup
    if $DEBUG
      begin
        product_detailhtml = File.open(File.join(@cache_dir, "#{asin}product_detail.html"), "w")
        product_detailhtml.flock(File::LOCK_EX)
        product_detailhtml.puts product_detail_scope
        product_detailhtml.flock(File::LOCK_UN)
        product_detailhtml.close
      rescue
        $stderr.puts $!.backtrace
      end
    end
    if %r'<b class=\'h1\'>仕様:</b><div class=\'content\'>(.+?)</div>'mis =~ product_detail_scope
      @product_detail = $1
    end
  end

  def get_review(asin)
    url = %Q'http://www.amazon.co.jp/gp/product/product-description/#{asin}/'
    begin
      request_body = download(url).to_s
      return if request_body.to_s.empty?
    rescue
      return
    end
    review_scope = ''
    if %r'<div class="bucket" id="productDescription">(.*)'mis =~ request_body
      review_scope = $1
    end
    if $DEBUG
      begin
        reviewhtml = File.open(File.join(@cache_dir, "#{asin}review.html"), "w")
        reviewhtml.flock(File::LOCK_EX)
        reviewhtml.puts review_scope
        reviewhtml.flock(File::LOCK_UN)
        reviewhtml.close
      rescue
        $stderr.puts $!.backtrace
      end
    end
    if %r'<b>(出版社.+?著者からの内容紹介)</b>(.*?)<(/div|b)>'mis =~ review_scope
      @reviewer =$1
      @review = $2
    elsif %r'<b>(内容（「.+?」データベースより）)</b>(.*?)<(/div|b)>'mis =~ review_scope
      @reviewer =$1
      @review = $2
    elsif %r'<b>(内容紹介)</b>(.*?)<(/div|b)>'mis =~ review_scope
      @reviewer = $1
      @review = $2
    elsif %r'<b>(商品紹介)</b>(.*?)<(/div|b)>'mis =~ review_scope
      @reviewer = $1
      @review = $2
    elsif %r'<b>(メーカーより)</b>(.*?)<(/div|b)>'mis =~ review_scope
      @reviewer = $1
      @review = $2
    elsif %r'<b>(Book Description)</b>(.*?)<(/div|b)>'mis =~ review_scope
      @reviewer = $1
      @review = $2
    elsif %r'<b>(メーカー/レーベルより)</b>(.*?)<(/div)>'mis =~ review_scope
      @reviewer = $1
      @review = $2
    elsif %r'<b>(Amazon.co.[^<]+?商品紹介)</b>(.*?)<(/div)>'mis =~ review_scope
      @reviewer = $1
      @review = $2
    end
    @reviewer = @reviewer.gsub(/<.+?>/,"").strip if ! @reviewer.empty?
    @review = @review.gsub(/<.+?>/,"").strip if ! @review.empty?
  end

  def download(url, proxy = nil)
    if /^http/ =~ url
      $stderr.puts "CHK; access; #{url}"
      require 'net/http'
      Net::HTTP.version_1_2
      domain, port = "", ""
      if /\/\/(.+?)(?:\/|$)/ =~ url
        domain = $1.to_s
      end
      protocol = url.split(":").first + "://"
      path = url.gsub(Regexp.new((protocol + domain).untaint),"")
      if ! proxy.nil?
        proxy, port = proxy.split(':')
        port = port.to_i
      else
        proxy = port = nil
      end
      begin
        http = Net::HTTP::Proxy(proxy, port).new(domain.untaint, 80)
        file_info = nil
        file_info_302 = true
        while file_info_302 do
          if /1\.6/ =~ RUBY_VERSION
            file_info, = http.get2(path)
          else
            file_info = http.get(path)
          end
          if file_info.code.to_i == 302
            path = file_info.header['location']
            path.gsub!(/#{protocol}#{domain}/,"")
            next
          end
          file_info_302 = false
          # Net::HTTP のバージョン 1.2 では、Net::HTTP#get メソッドは HTTPResponse を返します。
          #return file_info.response.body.gsub(/―/,"&minus;"), path
          return file_info.body.gsub(/―/,"&minus;"), path
        end
      rescue
        return 'net ' + $!
      end
    else
      return open(url, "rb").read.gsub(/―/,"&minus;"), nil
    end
  end

  def get_shopdata(code_id = nil, catalog = nil)
    return if code_id.nil?
    catalog = "asin" if catalog.nil?
    @catalog = catalog.strip.downcase
    cache_file_name = File.join(@cache_dir, "#{@catalog.upcase}-#{code_id}.yml")
    if FileTest.exist?(cache_file_name.untaint) # && ! $DEBUG
#      if noimage_retry_span.to_i > 0 then
#        if (Date.today - Date.parse(@retry_day.to_s)) < noimage_retry_span then
          load_cache(cache_file_name.untaint)
          return
#        else
#          puts "RETRY; Get no image information."
#        end
#      end
    end

    @srch_index = "blended"
    @srch_keywd = code_id
    @srch_url = "http://www.amazon.co.jp"
    platform_detail_get = false
    if /asin|isbn/ !~ @catalog
      case @catalog
      when "cd"
        #ポピュラー音楽
        @srch_index = "music-jp"
      when "dvd"
        #DVD
        @srch_index = "dvd-jp"
        platform_detail_get = true
      when "ps", "ps2", "psp", "gc", "xbox", "gba"
        #TVゲーム
        @srch_index = "videogames-jp"
        platform_detail_get = true
      when "toys"
        #おもちゃ&ホビー
        @srch_index = "toys-jp"
      when "software"
        #ソフトウェア
        @srch_index = "software-jp"
      when "sports"
        #スポーツ
        @srch_index = "sporting-goods-jp"
      when "electronics"
        #エレクトロニクス
        @srch_index = "electronics-jp"
      when "kitchen"
        #ホーム&キッチン</option>
        @srch_index = "kitchen-jp"
      when "vhs"
        #ビデオ
        @srch_index = "vhs-jp"
      end
      @srch_keywd = code_id.upcase
      @srch_url += %Q'/gp/search/?index=#{@srch_index}&field-keywords=#{@srch_keywd}'
    else
      code_id = code_id.delete('-')
      @srch_url += %Q'/exec/obidos/ASIN/#{code_id}/?val=authorized'
    end
    request_body = download(@srch_url).to_s

    if $DEBUG
      begin
        debug_cache_file_name = File.join(@cache_dir, "#{code_id}.html").untaint
        debug_cache = File.open(debug_cache_file_name.untaint, "w")
        debug_cache.flock(File::LOCK_EX)
        debug_cache.puts request_body
        debug_cache.flock(File::LOCK_UN)
        debug_cache.close
      rescue
        #
      end
    end

    image_scope = ''
    if %r'(^<b class="sans">(?:<b>)?.*(?:</b>)?<br>.*注文した商品はどこ[？?])'ms =~ request_body
      image_scope = $1
    elsif %r'(<div class="buying">.+注文した商品はどこ[？?])'ms =~ request_body
      image_scope = $1
    end
    if /isbn|asin/i =~ @catalog
      @srch_keywd = @srch_keywd.delete("-")
    end

    aws_request = ''
    if @use_aws4as2 && (! @aws_token.empty? && ! @associate_id.empty?)
      begin
        $:<< File.join(@ruby_amz_lib,'lib') if ! $:.include?(File.join(@ruby_amz_lib,'lib'))
        require 'aws4as2'
      rescue LoadError
        puts $!
      end
      begin
        aws = Aws.new(@aws_token, @associate_id)
        aws.power_search(@srch_keywd)
        aws_request = aws.as2request.to_s
      rescue
        puts $!
      end
    else
      # AmazonWebService を利用し、最低限、かつ不変の情報を配信するサイトから、情報をゲット
      awsurl = %Q'http://www.double-red.net/cgi-bin/aws/?srch_code=#{@srch_keywd}'
#      awsurl = %Q'http://cfc33/cgi-bin/amazon.cgi?srch_code=#{@srch_keywd}'
      begin
        aws_request = download(awsurl, nil).to_s
      rescue
        puts $!
      end
    end
    aws_request.each{|line|
      key = line.split(",").first
      value = line.gsub(/^@.+?,/,"").strip
      case key
      when /^@asin/
        @asin = value
      when /^@isbn/
        @isbn = value
      when /^@jancode/
        @jancode = value
      when /^@catalog/
        @catalog = value
      when /^@media/
        @media = value
      when /^@num_media/
        @num_media = value
      when /^@product_name/
        @product_name = value
      when /^@our_price/
        @our_price = value
      when /^@list_price/
        @list_price = value
      when /^@manufacturer/
        @manufacturer = value
      when /^@image_url_small/
        @image_url_small = value
      when /^@image_url_medium/
        @image_url_medium = value
      when /^@image_url_large/
        @image_url_large = value
      when /^@release_date/
        @release_date = value
      when /^@authors/
        @authors = value.split(",")
      when /^@directors/
        @directors = value.split(",")
      when /^@platforms/
        @platforms = value.split(",")
      when /^@starring/
        @starring = value.split(",")
      when /^@mpn/
        @mpn = value
      end
    }

    # リリース日が取得できなかった時
    if @release_date.to_s.empty?
      if %r"(?:<b>発売日[：:]</b>|\()([\d\/]+[\d])\)?<br>" =~ image_scope
        @release_date = $1
      elsif %r|\((\d{4}\/[\d\/]+)\)| =~ image_scope
        @release_date = $1
      end
    end

    # レーベル名を取得 by Tmas さん
    if %r'<b>類似商品をブラウズする：</b>' =~ image_scope
      if %r'#{@manufacturer}</a> &gt; <a(?:.+?)>(.+?)</a>'m =~ image_scope
        @book_label = $1
      end
    end

    # ゲームなどのプラットフォームゲット
    if /プラットフォーム:(.+?)$/ =~ request_body
      @platform = $1.to_s.gsub(/<.+?>/,"").gsub(/&nbsp;/,"")
    end

    # 紹介文、紹介者ゲット
    get_review(@asin)
    # 詳細仕様ゲット
    get_detail(@asin)

    w, h, fsize = get_imagesize(@image_url_medium)
    if w.to_i > 10 && h.to_i > 10 && fsize.to_i > 0
      @image_url_medium_wh = [w, h, fsize]
    else
      @image_url_medium = ''
    end
    w, h, fsize = get_imagesize(@image_url_large)
    if w.to_i > 10 && h.to_i > 10 && fsize.to_i > 0
      @image_url_large_wh = [w, h, fsize]
    else
      @image_url_large = ''
    end
    # ディスク枚数
    # @num_media = $1.gsub(/[^\d]/,"") if %r'(?:<b>)?ディスク枚数：(?:</b>)?(.+)<br>' =~ image_scope
    # 本サイズ
    @book_size = $1 if %r|サイズ\(cm\): ([^<]*)<?$| =~ image_scope
    if /book/i =~ @catalog
      @book_page = $1.strip if /\/b>(.+?)ページ</ =~ image_scope
    end
    if /dvd/i =~ @catalog
      @dvd_extra_spec = $1.strip if /<li><b>形式.+?<\/b>(.+?)</ =~ image_scope
      @dvd_region_code = $1.strip if /<b>リージョンコード.+?<\/b>(.+?)</ =~ image_scope
      @dvd_display_size = $1.strip if /<b>画面サイズ.+?<\/b>(.+?)</ =~ image_scope
      @dvd_play_time = $1.strip if /<b>時間.+?<\/b>(.+?)</ =~ image_scope
    end
	
    # キャッシュに保存
    save_cache(cache_file_name)
  end
end

class Filter
  def gen_template_file(amzinfo, str)
    begin
      templateF = File.join(amzinfo.cache_dir, %Q'#{amzinfo.asin}.html')
      File.chmod(0666, templateF) if FileTest.exist?(templateF) && FileTest.file?(templateF)
      template = File.open(templateF.untaint, "w")
      template.flock(File::LOCK_EX)
      template.puts str + "<!-- Generated by amazon_shop2 #{Amazon_shop2ver} -->"
      template.flock(File::LOCK_UN)
      template.close
    rescue
      $stderr.puts $!.backtrace
    end
  end

  def gen_tmpltstr(amzinfo, symbol)
    tagend = (@diary.isXHTML ? ' />' : '>')
    amazon_srch_url = %Q'http://www.amazon.co.jp/exec/obidos'

    begin
      ret_str = amzinfo.instance_variable_get(symbol)
    rescue
      $stderr.puts $!.backtrace
      return ''
    end
    case symbol
    when /image_url_medium$/
      width_m, height_m, fsize_m = amzinfo.image_url_medium_wh
      width_l, height_l, fsize_l = amzinfo.image_url_large_wh
      if width_m.to_i > 0 && height_m.to_i > 0
        fsize_m = fsize_m.to_i > 0 ? ",#{fsize_m}byte" : ""
        fsize_l = fsize_l.to_i > 0 ? ",#{fsize_l}byte" : ""
        image_size_m = "(#{width_m}x#{height_m}#{fsize_m})"
        image_size_l = "(#{width_l}x#{height_l}#{fsize_l})"
        alt = %Q'alt="#{amzinfo.product_name}#{image_size_m}" width="#{width_m}" height="#{height_m}"'
        if width_l.to_i > 0 && height_l.to_i > 0
          ret_str = %Q'<a href="#{amzinfo.image_url_large}" title="拡大イメージ#{image_size_l}"><img src ="#{amzinfo.image_url_medium}" #{alt}#{tagend}</a>'
        else
          ret_str = %Q'<img src ="#{amzinfo.image_url_medium}" #{alt}#{tagend}'
        end
      else
        ret_str = @diary.config['NO_IMAGE_FILE']
      end
    when /image_url_medium_wh/
      index = $1.to_i
      width, height, fsize = amzinfo.image_url_medium_wh
      if width.to_i > 0 && height.to_i > 0
        ret_str = %Q'#{width}x#{height}(#{fsize} byte)'
      end
    when /product_name/
      product_url = amzinfo.srch_url.gsub(/\?val=authorized/,"")
      # aid = @diary.config['BOOKSTORE_AMAZON_ID'].to_s.empty? ? '' : @diary.config['BOOKSTORE_AMAZON_ID']
      aid = amzinfo.associate_id
      aid = [aid, 'ref=nosim'].join('/')
      product_title = %Q' title="紹介ページへジャンプ"'
      ret_str = %Q'<a href="#{amazon_srch_url}/ASIN/#{amzinfo.asin}/#{aid}"#{product_title}>#{amzinfo.product_name}</a>'
    when /manufacturer/
      return '' if amzinfo.manufacturer.to_s.empty? || amzinfo.catalog.to_s.empty?
      manufacturer = amzinfo.manufacturer.strip
      manufacturer_title = %Q' title="#{manufacturer} の商品"'
      linkurl = CGI::escapeHTML(%Q'#{amazon_srch_url}/search-handle-url/?index=#{CGI::escape(amzinfo.srch_index)}&field-keywords=#{manufacturer.myescape}')
      anchor = %Q'<a href="#{linkurl}" #{manufacturer_title}>#{manufacturer}</a>'
      if /book/i =~ amzinfo.catalog
        label = amzinfo.book_label.to_s.empty? ? '' : "(#{amzinfo.book_label})"
        ret_str = %Q'出版社: #{anchor}#{label}'
      else
        ret_str = %Q'メーカー: #{anchor}'
      end
    when /authors/
      return '' if amzinfo.authors.to_s.empty?
      author_info = []
      amzinfo.authors.each{|author|
        author.strip!
        url = CGI::escapeHTML(%Q'#{amazon_srch_url}/search-handle-url/index=#{CGI::escape(amzinfo.srch_index)}&field-keywords=#{author.strip.myescape}')
        author_info << %Q'<a href="#{url}" title="#{author} の作品">#{author}</a>'
      }
      ret_str = %Q'著者: #{author_info.join(",")}'
    when /directors/
      return '' if amzinfo.directors.to_s.empty?
      director_info = []
      amzinfo.directors.each{|director|
        director.strip!
        url = CGI::escapeHTML(%Q'#{amazon_srch_url}/search-handle-url/index=#{CGI::escape(amzinfo.srch_index)}&field-keywords=#{director.strip.myescape}')
        director_info << %Q'<a href="#{url}" title="#{director} の作品">#{director}</a>'
      }
      ret_str = %Q'監督: #{director_info.join(",")}'
    when /starring/
      return '' if amzinfo.starring.to_s.empty?
      actor_info = []
      amzinfo.starring.each{|actor|
        actor.strip!
        url = CGI::escapeHTML(%Q'#{amazon_srch_url}/search-handle-url/index=#{CGI::escape(amzinfo.srch_index)}&field-keywords=#{actor.myescape}')
        actor_info << %Q'<a href="#{url}" title="#{actor} の作品">#{actor}</a>'
      }
      ret_str = %Q'出演: #{actor_info.join(",")}'
    when /review$/
      return '' if amzinfo.review.to_s.empty?
      review_str = amzinfo.review.dup
      if review_str.size > 100 then
        review_str.gsub!(/(.{100}.+?)[ 。、].*/mi){
          $1 + %Q'<a href="#{product_url}" title="続きは商品ページ">(...)</a>'
        }
      end
      ret_str = %Q'#{review_str}'
    when /product_detail/
      return '' if amzinfo.product_detail.to_s.empty?
      product_detail = amzinfo.product_detail.dup
      if product_detail.size > 100 then
        product_detail.gsub!(/<.?ul>/i,"")
        product_detail.gsub!(/<.?b>/i,"")
        product_detail.gsub!(/(.{60}.+?)<.+?>.*/mi){
          $1 + %Q'<a href="#{product_url}" title="続きは商品ページ">(...)</a>'
        }
      end
      ret_str = %Q'<ul>#{product_detail}</ul>'
    when /num_media/
      return '' if amzinfo.num_media.to_s.empty? || amzinfo.catalog.to_s.empty?
      if /dvd|cd|music/i !~ amzinfo.catalog
        ret_str = %Q'数量: #{amzinfo.num_media}'
      else
        ret_str = %Q'枚数: #{amzinfo.num_media}'
      end
    end
    return ret_str
  end

  def amzinfo_pretty(amzinfo)
    as2_template_file = File.join(amzinfo.cache_dir, 'template.txt')
    str = Array.new
    if @diary.config['AMAZON_SHOP_TEMPLATE'] && FileTest.exist?(as2_template_file)
      # テンプレート
      template = open(as2_template_file, "r").read
      template.tosjis.each{|line|
        next if /^#/ =~ line
        line.gsub!(/(\[.+?\])/){
          eval_str = $1
          symbol, before, after = eval(eval_str)
          value = ''
          if amzinfo.instance_variables.include?(symbol)
            value = amzinfo.instance_variable_get(symbol)
          end
          begin
            value = gen_tmpltstr(amzinfo, symbol)
            next if value.to_s.empty?
            before + value + after if ! value.to_s.empty?
          rescue
            #
          end
        }
        line.chop!
        str << line if ! line.empty?
      }
      gen_template_file(amzinfo, str.join("\r\r"))
      return
    end
    # amazon_shop2 デフォルト表示の時は強制でケツに <br> タグ付与する
    info_str = Array.new # あとでまとめて join するので、Array
    # 商品画像
    image_str = gen_tmpltstr(amzinfo, "@image_url_medium")

    # 商品名
    product_anchor =  gen_tmpltstr(amzinfo, "@product_name")
    if amzinfo.platform.to_s.empty?
      info_str << %Q'『#{product_anchor}』#{amzinfo.media.empty? ? amzinfo.catalog : amzinfo.media}' + @diary.tag_br
    else
      info_str << %Q'『#{product_anchor}』#{amzinfo.platform}' + @diary.tag_br
    end

    # 会社名
    if ! amzinfo.manufacturer.empty? # 会社名
      info_str << gen_tmpltstr(amzinfo, "@manufacturer") + @diary.tag_br
    end

    # 著者
    if ! amzinfo.authors.to_s.empty?
      info_str << gen_tmpltstr(amzinfo, "@authors") + @diary.tag_br
    end

    # 監督
    if ! amzinfo.directors.to_s.empty?
      info_str << gen_tmpltstr(amzinfo, "@directors") + @diary.tag_br
    end

    # 出演者
    if ! amzinfo.starring.to_s.empty?
      info_str << gen_tmpltstr(amzinfo, "@starring") + @diary.tag_br
    end
    # 発売日、価格、ディスク枚数をまとめて表示
    r, p, s, pg = nil,nil,nil,nil
    r = %Q'発売日: #{amzinfo.release_date}' if ! amzinfo.release_date.empty?
    p = %Q'価格: #{amzinfo.our_price}' if ! amzinfo.our_price.empty?
    pg = %Q'#{amzinfo.book_page}ページ'  if ! amzinfo.book_page.empty?
    s = %Q'サイズ: #{amzinfo.book_size}cm' if ! amzinfo.book_size.empty?
    s = %Q'#{gen_tmpltstr(amzinfo, "@num_media")}' if ! amzinfo.num_media.to_s.empty?
    rps = [r,pg,p,s]
    rps.delete_if{|x| x.nil?}
    info_str << rps.join(", ") + @diary.tag_br

    # DVD のスペックをまとめて表示
    r, p, s = nil,nil,nil,nil
    r = %Q'形式: #{amzinfo.dvd_extra_spec}' if ! amzinfo.dvd_extra_spec.empty?
    p = %Q'画面サイズ: #{amzinfo.dvd_display_size}' if ! amzinfo.dvd_display_size.empty?
    s = %Q'時間: #{amzinfo.dvd_play_time}' if ! amzinfo.dvd_play_time.empty?
    rps = [r,p,s]
    rps.delete_if{|x| x.nil?}
    info_str << rps.join(", ") + @diary.tag_br

    # 商品レビュー
    if ! amzinfo.reviewer.empty? && ! amzinfo.review.empty?
      info_str << %Q'【#{amzinfo.reviewer}】' + @diary.tag_br
      info_str << gen_tmpltstr(amzinfo, "@review") + @diary.tag_br
    end

    # 詳細スペック
    if ! amzinfo.product_detail.to_s.empty?
      info_str << gen_tmpltstr(amzinfo, "@product_detail") + @diary.tag_br
    end

    str << %Q'\t<div class="amazon">'
    str << %Q'\t\t<table style="text-align:left;" summary="Product Information">'
    str << %Q'\t\t\t<tr>'
    str << %Q'\t\t\t\t<td>#{image_str}</td>'
    str << %Q'\t\t\t\t<td>'
    str << %Q'\t\t\t\t\t#{info_str.join("\r\r")}'
    str << %Q'\t\t\t\t</td>'
    str << %Q'\t\t\t</tr>'
    str << %Q'\t\t</table>'
    str << %Q'\t</div>'
    str = str.join("\r\r").to_s.gsub(/^ +\n/m,"")
    gen_template_file(amzinfo, str)
  end

  def amazon_shop2(str, type)
    amzinfo = AmazonShop2.new
    amzinfo.cache_dir = File.join(@diary.logDirectory, 'amazon_shop2')
    amzinfo.diary_day = @diary.date
    # $stderr.puts "umask: #{File::umask}" if $DEBUG
    File.umask(022)
    FileUtils.mkdir_p(amzinfo.cache_dir) if ! FileTest.exist?(amzinfo.cache_dir)
    # $stderr.puts "cache dir mode : #{File.stat(amzinfo.cache_dir).mode}" if $DEBUG
    File.chmod(16877,amzinfo.cache_dir)

    amzinfo.proxy = @diary.config['PROXY'].to_s.empty? ? '' : @diary.config['PROXY']
    amzshop2_key = 'CD|DVD|PS|PS2|PSP|GC|GBA|NDS|XBOX|ASIN|ISBN|JAN|MAGAZINE'
    amzshop2_key = @diary.config['AMAZON_CATALOG'] if ! @diary.config['AMAZON_CATALOG'].to_s.empty?

    amzshop2_prefix = '@@'
    amzshop2_prefix = @diary.config['AMAZON_SHOP2_PREFIX'] if ! @diary.config['AMAZON_SHOP2_PREFIX'].to_s.empty?

    # 商品情報取得に aws4as2 を使うか?
    amzinfo.use_aws4as2 = true if /^true$/i =~ @diary.config['USE_AWS4AS2'].to_s
    if amzinfo.use_aws4as2
      user_lib = @diary.config['USER_LIB_DIRECTORY'].to_s
      if ! user_lib.empty?
        $: << user_lib if ! $:.include?(user_lib)
      end
      ruby_amz = (File.join(user_lib,"ruby-amazon","lib"))
      $: << ruby_amz if ! $:.include?(ruby_amz)
    end
    # デベロッパートークンセット
    # BOOKSTORE_AMAZON_TOKEN か、AMAZON_DEV_TOKEN どちらかを受付ける。
    amzinfo.aws_token = @diary.config['BOOKSTORE_AMAZON_TOKEN'].to_s.empty? ? '' : @diary.config['BOOKSTORE_AMAZON_TOKEN']
    if amzinfo.aws_token.empty? && ! @diary.config['AMAZON_DEV_TOKEN'].to_s.empty?
      amzinfo.aws_token =  @diary.config['AMAZON_DEV_TOKEN'].to_s
    end
    # アソシエイト ID セット
    # BOOKSTORE_AMAZON_ID か、AMAZON_ASSOCIATE_ID どちらかを受付ける。
    amzinfo.associate_id = @diary.config['BOOKSTORE_AMAZON_ID'].to_s.empty? ? '' : @diary.config['BOOKSTORE_AMAZON_ID']
    if amzinfo.associate_id.empty? && ! @diary.config['AMAZON_ASSOCIATE_ID'].to_s.empty?
      amzinfo.associate_id =  @diary.config['AMAZON_ASSOCIATE_ID'].to_s
    end
    amzinfo.noimage_retry_span = @diary.config['AMAZON_NOIMAGE_RETRY_SPAN'].to_i

    case type
    when :P
      templateF = ''
      str.gsub!(/\(#{amzshop2_prefix}(#{amzshop2_key}):(.+?)(\/.+?)?\)/i){
        catalog = $1
        asin = $2
        begin
          # ここで失敗すると何も表示されないので致命的
          amzinfo.get_shopdata(asin, catalog)
        rescue
          raise "get_shopdata error : " + $!.backtrace
        end
        amzinfo_pretty(amzinfo)
        templateF = File.join(amzinfo.cache_dir, "#{amzinfo.asin}.html")
        newstr = ''
        if FileTest.exist?(templateF)
          template = File.open(templateF, "r")
          newstr = template.read
          template.close
        else
          $stderr.puts "Not Found : #{templateF}"
        end
        newstr.chop!
      }
      # 後始末
      begin
        FileUtils.rm_f(templateF) if FileTest.exist?(templateF)
      rescue
        # 削除に失敗しても気にするな
        $stderr.puts $!.backtrace
      end
      str
    when :HTML
      str.gsub!("\r\r","\n")
      str.gsub!(/<p(?: +id=\"\w+?\")?>\s*<\/p>/mi, '')
      str
    end
  end
end
