aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorToshiaki Asai <toshi.alternative@gmail.com>2016-10-08 09:58:53 +0900
committerToshiaki Asai <toshi.alternative@gmail.com>2016-10-09 10:52:01 +0900
commitda38691b8b73794d6fe998d2ad5ce48888995f75 (patch)
tree1ef99f618cdd4af46f8cf3c2cca01a77c79926ce
parent41f1bba3e28cc0c94d4af8fb0a3fc7d834b516d1 (diff)
downloadmikutter-da38691b8b73794d6fe998d2ad5ce48888995f75.tar.gz
Albumに画像のダウンロード機能を移植 refs #900
-rw-r--r--core/plugin/openimg/model/album.rb131
-rw-r--r--core/plugin/openimg/openimg.rb34
-rw-r--r--core/plugin/openimg/window.rb34
3 files changed, 153 insertions, 46 deletions
diff --git a/core/plugin/openimg/model/album.rb b/core/plugin/openimg/model/album.rb
index a120ff2f..d377d044 100644
--- a/core/plugin/openimg/model/album.rb
+++ b/core/plugin/openimg/model/album.rb
@@ -4,7 +4,8 @@ module Plugin::Openimg
class Album < Retriever::Model
register :openimg_photo, name: Plugin[:openimg]._('画像ビューア')
- field.uri :perma_link
+ field.uri :perma_link
+ field.string :blob
handle ->uri{
uri_str = uri.to_s
@@ -13,5 +14,133 @@ module Plugin::Openimg
} do |uri|
new(perma_link: uri)
end
+
+ # 画像をダウンロードする。
+ # partialを指定すると、ダウンロードの進捗があれば、前回呼び出されたときから
+ # ダウンロードできた内容を引数に呼び出される。
+ # 既にダウンロードが終わっていれば、 _blob_ の戻り値がそのまま渡される。
+ # このメソッドは、複数回呼び出されても画像のダウンロードを一度しか行わない。
+ # ==== Args
+ # [&partial_callback] 現在ダウンロードできたデータの一部(String)
+ # ==== Return
+ # [Delayer::Deferred::Deferredable] ダウンロードが完了したらselfを引数に呼び出される
+ def download(&partial_callback) # :yield: part
+ case @state
+ when :complete
+ partial_callback.(blob) if block_given?
+ Delayer::Deferred.new.next{ self }
+ when :download
+ append_download_queue(&partial_callback)
+ else
+ download!(&partial_callback)
+ end
+ end
+
+ # 画像のダウンロードが終わっていれば真を返す。
+ # 真を返す時、 _blob_ には完全な画像の情報が存在している
+ def completed?
+ @state == :complete
+ end
+
+ # 画像をダウロード中なら真
+ def downloading?
+ @state == :download
+ end
+
+ # ダウンロードが始まっていなければ真
+ def ready?
+ !@state
+ end
+
+ private
+
+ def download!(&partial_callback)
+ atomic do
+ return download(&partial_callback) unless ready?
+ promise = initialize_download(&partial_callback)
+ Thread.new(&gen_download_routine).next{|success|
+ if success
+ finalize_download_as_success
+ else
+ Delayer::Deferred.fail false
+ end
+ }.trap{|exception|
+ finalize_download_as_fail(exception)
+ }.terminate('error')
+ promise
+ end
+ end
+
+ def append_download_queue(&partial_callback)
+ atomic do
+ return download(&partial_callback) unless downloading?
+ register_partial_callback(partial_callback)
+ register_promise
+ end
+ end
+
+ def register_promise
+ promise = Delayer::Deferred.new(true)
+ (@promises ||= Set.new) << promise
+ promise
+ end
+
+ def register_partial_callback(cb)
+ @partials ||= Set.new
+ if cb
+ @partials << cb
+ cb.(@buffer) if !@buffer.empty?
+ end
+ end
+
+ def gen_download_routine
+ -> do
+ begin
+ _, raw = Plugin.filtering(:openimg_raw_image_from_display_url, perma_link.to_s, nil)
+ if raw
+ download_mainloop(raw)
+ else
+ raise "couldn't resolve actual image url of #{perma_link}."
+ end
+ rescue EOFError
+ true
+ ensure
+ raw.close rescue nil
+ end
+ end
+ end
+
+ def download_mainloop(raw)
+ loop do
+ Thread.pass
+ partial = raw.readpartial(1024*HYDE).freeze
+ @buffer << partial
+ atomic{ @partials.each{|c|c.(partial)} }
+ end
+ end
+
+ def initialize_download(&partial_callback)
+ @state = :download
+ @buffer = String.new
+ register_partial_callback(partial_callback)
+ register_promise
+ end
+
+ def finalize_download_as_success
+ atomic do
+ self.blob = @buffer.freeze
+ @state = :complete
+ @promises.each{|p| p.call(self) }
+ @buffer = @promises = @partials = nil
+ end
+ end
+
+ def finalize_download_as_fail(exception)
+ atomic do
+ @state = nil
+ @promises.each{|p| p.fail(exception) }
+ @buffer = @promises = @partials = nil
+ end
+ end
end
end
diff --git a/core/plugin/openimg/openimg.rb b/core/plugin/openimg/openimg.rb
index db0f245d..4af451a1 100644
--- a/core/plugin/openimg/openimg.rb
+++ b/core/plugin/openimg/openimg.rb
@@ -44,30 +44,10 @@ Plugin.create :openimg do
error _
nil end end
- filter_openimg_pixbuf_from_display_url do |display_url, loader, thread|
- raw = Plugin.filtering(:openimg_raw_image_from_display_url, display_url, nil).last
- if raw
- begin
- loader = Gdk::PixbufLoader.new
- thread = Thread.new do
- begin
- loop do
- Thread.pass
- partial = raw.readpartial(1024*HYDE)
- atomic{ loader.write partial }
- end
- nil
- rescue EOFError
- true
- ensure
- raw.close rescue nil
- loader.close rescue nil end end
- [display_url, loader, thread]
- rescue => _
- error _
- [display_url, loader, thread] end
- else
- [display_url, loader, thread] end end
+ filter_openimg_pixbuf_from_display_url do |album, loader, thread|
+ loader = Gdk::PixbufLoader.new
+ [album, loader, album.download{|partial| atomic{ loader.write partial } }]
+ end
filter_openimg_raw_image_from_display_url do |display_url, content|
unless content
@@ -80,11 +60,11 @@ Plugin.create :openimg do
[display_url, content] end
on_openimg_open do |display_url|
- Plugin::Openimg::Window.new(display_url).start_loading.show_all
+ Plugin.call(:open, display_url)
end
- intent Plugin::Openimg::Album do |intent|
- Plugin.call(:openimg_open, intent.model.perma_link.to_s)
+ intent Plugin::Openimg::Album do |intent_token|
+ Plugin::Openimg::Window.new(intent_token.model, intent_token).start_loading.show_all
end
def addsupport(cond, element_rule = {}, &block); end
diff --git a/core/plugin/openimg/window.rb b/core/plugin/openimg/window.rb
index 1f2e4a72..f1b59838 100644
--- a/core/plugin/openimg/window.rb
+++ b/core/plugin/openimg/window.rb
@@ -2,45 +2,43 @@
module Plugin::Openimg
class Window < Gtk::Window
- attr_reader :display_url
+ attr_reader :album
- def initialize(display_url)
+ def initialize(album, next_opener)
super()
- @display_url = display_url
+ @album = album
@image_surface = loading_surface
+ @next_opener = next_opener
window_settings
ssc(:destroy, &:destroy)
end
def start_loading
Thread.new {
- Plugin.filtering(:openimg_pixbuf_from_display_url, display_url, nil, nil)
- }.next { |_, pixbufloader, thread|
+ Plugin.filtering(:openimg_pixbuf_from_display_url, album, nil, nil)
+ }.next { |_, pixbufloader, complete_promise|
if pixbufloader.is_a? Gdk::PixbufLoader
pixbufloader.ssc(:area_updated, window) do |_, x, y, width, height|
Delayer.new do
- if thread.alive?
- progress(pixbufloader.pixbuf, x: x, y: y, width: width, height: height) end end
+ progress(pixbufloader.pixbuf, x: x, y: y, width: width, height: height) end
true end
pixbufloader.ssc(:closed, window) do
progress(pixbufloader.pixbuf, paint: true)
true end
- thread.next { |flag|
- Deferred.fail flag unless flag
- }.trap { |exception|
- error exception
+ complete_promise.trap { |exception|
@image_surface = error_surface
+ redraw(repaint: true)
}
else
- warn "cant open: #{display_url}"
+ warn "cant open: #{album}"
@image_surface = error_surface
- redraw(repaint: false) end
+ redraw(repaint: true) end
}.trap{ |exception|
error exception
@image_surface = error_surface
- redraw(repaint: false)
+ redraw(repaint: true)
}
self
end
@@ -48,7 +46,7 @@ module Plugin::Openimg
private
def window_settings
- set_title(display_url)
+ set_title(album.perma_link.to_s)
set_role('mikutter_image_preview'.freeze)
set_type_hint(Gdk::Window::TYPE_HINT_DIALOG)
set_default_size(*default_size)
@@ -128,14 +126,14 @@ module Plugin::Openimg
def gen_browser_clicked
proc do
- Plugin.call(:open, @display_url)
+ Plugin.call(:open, @next_opener)
false
end
end
def gen_wrap_expose_event
proc do |widget|
- redraw(repaint: false)
+ redraw(repaint: true)
true
end
end
@@ -145,7 +143,7 @@ module Plugin::Openimg
proc do |widget|
if widget.window && last_size != widget.window.geometry[2,2]
last_size = widget.window.geometry[2,2]
- redraw(repaint: false)
+ redraw(repaint: true)
end
false
end