diff options
author | Toshiaki Asai <toshi.alternative@gmail.com> | 2016-10-08 09:58:53 +0900 |
---|---|---|
committer | Toshiaki Asai <toshi.alternative@gmail.com> | 2016-10-09 10:52:01 +0900 |
commit | da38691b8b73794d6fe998d2ad5ce48888995f75 (patch) | |
tree | 1ef99f618cdd4af46f8cf3c2cca01a77c79926ce | |
parent | 41f1bba3e28cc0c94d4af8fb0a3fc7d834b516d1 (diff) | |
download | mikutter-da38691b8b73794d6fe998d2ad5ce48888995f75.tar.gz |
Albumに画像のダウンロード機能を移植 refs #900
-rw-r--r-- | core/plugin/openimg/model/album.rb | 131 | ||||
-rw-r--r-- | core/plugin/openimg/openimg.rb | 34 | ||||
-rw-r--r-- | core/plugin/openimg/window.rb | 34 |
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 |