diff --git a/lib/ferrum/browser.rb b/lib/ferrum/browser.rb index 9626ad1d..da81f6e0 100644 --- a/lib/ferrum/browser.rb +++ b/lib/ferrum/browser.rb @@ -22,6 +22,7 @@ class Browser body doctype content= headers cookies network downloads mouse keyboard + start_screencast stop_screencast screenshot pdf mhtml viewport_size device_pixel_ratio frames frame_by main_frame evaluate evaluate_on evaluate_async execute evaluate_func diff --git a/lib/ferrum/page.rb b/lib/ferrum/page.rb index c019b831..5ee121e3 100644 --- a/lib/ferrum/page.rb +++ b/lib/ferrum/page.rb @@ -7,10 +7,12 @@ require "ferrum/headers" require "ferrum/cookies" require "ferrum/dialog" +require "ferrum/screencaster" require "ferrum/network" require "ferrum/downloads" require "ferrum/page/frames" require "ferrum/page/screenshot" +require "ferrum/page/screencast" require "ferrum/page/animation" require "ferrum/page/tracing" require "ferrum/page/stream" @@ -30,6 +32,7 @@ class Page include Screenshot include Frames include Stream + include Screencast attr_accessor :referrer attr_reader :context_id, :target_id, :event, :tracing @@ -69,12 +72,16 @@ class Page # @return [Downloads] attr_reader :downloads + # Control Screencasting + # + # @return [nil] + attr_reader :screencaster + def initialize(client, context_id:, target_id:, proxy: nil) @client = client @context_id = context_id @target_id = target_id @options = client.options - @frames = Concurrent::Map.new @main_frame = Frame.new(nil, self) @event = Utils::Event.new.tap(&:set) @@ -87,6 +94,7 @@ def initialize(client, context_id:, target_id:, proxy: nil) @network = Network.new(self) @tracing = Tracing.new(self) @downloads = Downloads.new(self) + @screencaster = Screencaster.new(self) subscribe prepare_page @@ -388,6 +396,10 @@ def on(name, &block) request = Network::AuthRequest.new(self, params) block.call(request, index, total) end + when :screencastFrame + @client.on("Page.screencastFrame") do |params, index, total| + block.call(params, index, total) + end else client.on(name, &block) end @@ -441,6 +453,11 @@ def subscribe dialog.accept end end + + on(:screencastFrame) do |params, _index, _total| + @screencaster.add_frame(params) + warn "DEBUG: frame added in page.rb" + end end def prepare_page diff --git a/lib/ferrum/page/screencast.rb b/lib/ferrum/page/screencast.rb new file mode 100644 index 00000000..1ad3a02f --- /dev/null +++ b/lib/ferrum/page/screencast.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +require "ferrum/screencaster" + +module Ferrum + class Page + module Screencast + attr_reader :screencaster + + def start_screencast + @screencaster.start_screencast + end + + def stop_screencast + @screencaster.stop_screencast + end + end + end +end diff --git a/lib/ferrum/screencaster.rb b/lib/ferrum/screencaster.rb new file mode 100644 index 00000000..c70ea716 --- /dev/null +++ b/lib/ferrum/screencaster.rb @@ -0,0 +1,47 @@ +# frozen_string_literal: true +require 'fileutils' + +# Combine the resulting frames together into a video with: +# ffmpeg -y -framerate 30 -i 'frame-%d.jpeg' -c:v libx264 -r 30 -pix_fmt yuv420p try2.mp4 +# +module Ferrum + class Screencaster + def initialize(page) + @page = page + @frame_number = 0 + @threads = [] + @base_dir = "" + end + + def add_frame(params) + warn "frame" + + @page.command("Page.screencastFrameAck", sessionId: params["sessionId"]) + + t = Thread.new { File.binwrite("#{recordings_dir}/frame-#{@frame_number}.jpeg", Base64.decode64(params["data"])) } + @frame_number += 1 + @threads << t + true + end + + def recordings_dir + return @recordings_dir if defined? @recordings_dir + + session_id = @page.client.session_id + @recordings_dir = FileUtils.mkdir_p("#{@base_dir}/screencast_recordings/#{session_id}/").first + @recordings_dir + end + + def start_screencast(base_dir = "") + @base_dir = base_dir + @page.command("Page.startScreencast", format: "jpeg") + end + + def stop_screencast + warn "joining threads" + @threads.each(&:join) + warn "stopped" + @page.command("Page.stopScreencast") + end + end +end