Skip to content

Instantly share code, notes, and snippets.

@tana
Created September 23, 2011 14:05
Show Gist options
  • Select an option

  • Save tana/1237420 to your computer and use it in GitHub Desktop.

Select an option

Save tana/1237420 to your computer and use it in GitHub Desktop.
mikutter icon memory cache
# -*- coding: utf-8 -*-
require File.expand_path('utils')
miquire :core, 'environment'
miquire :mui, 'skin'
require 'gtk2'
require 'net/http'
require 'uri'
require 'digest/md5'
require 'thread'
require 'observer'
# メモリキャッシュ対応用メモ
# cache:filename の場合はメモリキャッシュ
# メモリキャッシュ Hash + 容量制限
class MemoryCache
@@hash = {}
@@keys = []
@@size = 0
def MemoryCache.get(filename)
@@hash[filename]
end
def MemoryCache.set(filename, value)
size = value.length
@@keys << [filename, value.length]
@@size = @@size + size
if (@@size > 1000000) # 1000000はサイズ制限
@@size = @@size - @@keys[0][1]
@@hash.delete(@@keys.shift[0])
#puts "memory cache deleted (@@hash.length=#{@@hash.length}, @@size=#{@@size})"
end
@@hash[filename] = value
end
def MemoryCache.exist?(filename)
return @@hash.key?(filename)
end
end
class File
@@old_exist = File.method(:exist?)
def File.exist?(filename)
if (filename.index("cache:") == 0)
MemoryCache.exist?(filename)
else
return @@old_exist.call(filename)
end
end
@@old_expand = File.method(:expand_path)
def File.expand_path(filename)
if (filename.index("cache:") == 0)
filename
else
@@old_expand.call(filename)
end
end
end
module FileTest
@@old_exist = FileTest.method(:exist?)
def FileTest.exist?(filename)
if (filename.index("cache:") == 0)
MemoryCache.exist?(filename)
else
return @@old_exist.call(filename)
end
end
end
def create_pixbuf(filename, width, height)
if (filename.index("cache:") == 0)
# メモリキャッシュから読み込み
loader = Gdk::PixbufLoader.new
loader.set_size(width, height)
loader.write MemoryCache.get(filename)
loader.close
pixbuf = loader.pixbuf
return pixbuf
else
return Gdk::Pixbuf.new(filename, width, height)
end
end
module Gtk
class WebIcon < Image
include Observable
attr_reader :loading_thread
#ICONDIR = "#{Environment::CONFROOT}icons#{File::SEPARATOR}"
ICONDIR = "/media/MYLINUXLIVE/mikutter/iconcache/"
@@image_download_lock = Mutex.new
@@m_iconlock = Mutex.new
@@l_iconring = Hash.new{ Mutex.new }
@@pixbuf = @@oldpixbuf = Hash.new{ Hash.new{ Hash.new } }
@@pixbufcache_lastcleartime = Time.now
def initialize(img, width=48, height=48)
@loading_thread = nil
if(img.index('http://') == 0)
filename = WebIcon.get_filename(img)
if not(File.exist?(filename)) then
@loading_thread = WebIcon.iconring(img, [width, height]){ |pic|
if destroyed?
notice "object destroyed"
else
self.pixbuf = pic
self.changed
self.notify_observers end }
filename = File.expand_path(MUI::Skin.get("loading.png"))
end
img = filename
end
super(WebIcon.genpixbuf(img, width, height))
end
def self.remove_cache(*args)
end
def self.get_icon_pixbuf(img, width=48, height=width, &onload)
type_strict img => String
if(img.index('http://') == 0)
filename = WebIcon.get_filename(img)
if not(File.exist?(filename)) then
iconring(img, [width, height], &onload)
filename = File.expand_path(MUI::Skin.get("loading.png")) end
img = filename end
WebIcon.genpixbuf(img, width, height) end
# URL _img_ の画像を読み込む。読み込みが終わったら、ブロックにGdk::Pixbufを取って呼び出す
# Gdk::Pixbufは _dim_ で指定された寸法(px)に収まるようにリサイズされて渡される
# ロードをしているThreadを返す
def self.iconring(img, dim=[48,48], &onload)
Thread.new{
WebIcon.background_icon_loader(img, dim, &onload) } end
def self.get_filename(url)
ext = (File.extname(url).split("?", 2)[0] or File.extname(url))
File.expand_path(self.icondir + Digest::MD5.hexdigest(url) + ext)
end
def self.pixbuf_cache_get(filename, width, height)
if @@pixbuf[filename][width][height]
@@pixbuf[filename][width][height]
elsif @@oldpixbuf[filename][width][height]
@@pixbuf[filename][width][height] = @@oldpixbuf[filename][width][height] end end
def self.pixbuf_cache_set(filename, pixbuf)
if @@pixbufcache_lastcleartime < Time.now
notice 'pixbuf cache refreshed'
@@pixbufcache_lastcleartime = Time.now + 60 * 30
@@oldpixbuf = @@pixbuf
@@pixbuf = Hash.new{ Hash.new{ Hash.new } } end
@@pixbuf[filename][pixbuf.width][pixbuf.height] = pixbuf end
def self.genpixbuf(filename, width=48, height=48)
result = nil
if FileTest.exist?(File.expand_path(filename))
begin
@@m_iconlock.synchronize{
result = pixbuf_cache_get(filename, width, height)
if not(result.is_a?(Gdk::Pixbuf))
result = pixbuf_cache_set(filename,
#Gdk::Pixbuf.new(File.expand_path(filename), width, height)
create_pixbuf(File.expand_path(filename), width, height)
)
end
}
rescue Gdk::PixbufError
#puts "PIXBUFERROR!!!!!!!"
result = Gdk::Pixbuf.new(File.expand_path(MUI::Skin.get('notfound.png')), width, height) end end
result end
def self.background_icon_loader(img, dim=[48,48], &onload)
filename = WebIcon.local_path(img)
Delayer.new(Delayer::LATER){
Lock.synchronize{
onload.call(self.genpixbuf(filename, *dim)) } } end
# アイコンキャッシュのファイル名をフルパスで返す
def self.local_path(url)
@@l_iconring[url].synchronize{
filename = WebIcon.get_filename(url)
if not(FileTest.exist?(filename)) then
begin
res = Net::HTTP.get_response(URI.parse(url))
if(res.is_a?(Net::HTTPResponse)) and (res.code == '200') then
# ここでファイルに保存している
# ここをメモリ上に入れる処理に書き換える
#open(filename, 'wb'){ |f|
# f.write res.body
#}
MemoryCache.set("cache:" + filename, res.body)
filename = "cache:" + filename
else
#puts "NOTEXIST!!!!!!!"
filename = MUI::Skin.get("notfound.png")
end
rescue Timeout::Error, StandardError => e
#puts "TIMEOUT!!!!!"
#puts "----------------"
#puts e.message
#puts e.backtrace
#exit
filename = MUI::Skin.get("notfound.png")
end
end
filename
}
end
def self.icondir
if not(FileTest.exist?(File.expand_path(ICONDIR)))
FileUtils.mkdir_p File.expand_path(ICONDIR)
end
ICONDIR
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment