From 14666cac001dabde976c3f29ba64dcfedaa73535 Mon Sep 17 00:00:00 2001 From: designateddolt Date: Mon, 17 Apr 2023 23:43:41 +1200 Subject: [PATCH 1/5] Implement screencast --- lib/ferrum/browser.rb | 1 + lib/ferrum/page.rb | 19 +++++++++++++- lib/ferrum/page/screencast.rb | 19 ++++++++++++++ lib/ferrum/screencaster.rb | 47 +++++++++++++++++++++++++++++++++++ 4 files changed, 85 insertions(+), 1 deletion(-) create mode 100644 lib/ferrum/page/screencast.rb create mode 100644 lib/ferrum/screencaster.rb diff --git a/lib/ferrum/browser.rb b/lib/ferrum/browser.rb index 5f609a62..116a6de6 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 8f689ae4..46f73d74 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 @@ -374,6 +382,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 @@ -425,6 +437,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 From a452215da6338ce66fe4396b71d059803687014a Mon Sep 17 00:00:00 2001 From: Peter Singh Date: Sat, 4 Jan 2025 20:54:20 +0000 Subject: [PATCH 2/5] Fixes for running with Cuprite on v0.15 --- lib/ferrum/page/screencast.rb | 4 ++-- lib/ferrum/screencaster.rb | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/ferrum/page/screencast.rb b/lib/ferrum/page/screencast.rb index 1ad3a02f..7638f785 100644 --- a/lib/ferrum/page/screencast.rb +++ b/lib/ferrum/page/screencast.rb @@ -7,8 +7,8 @@ class Page module Screencast attr_reader :screencaster - def start_screencast - @screencaster.start_screencast + def start_screencast(...) + @screencaster.start_screencast(...) end def stop_screencast diff --git a/lib/ferrum/screencaster.rb b/lib/ferrum/screencaster.rb index c70ea716..3fbc801c 100644 --- a/lib/ferrum/screencaster.rb +++ b/lib/ferrum/screencaster.rb @@ -27,8 +27,8 @@ def add_frame(params) 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 + timestamp = (Time.now.to_f * 1000).to_i + @recordings_dir = FileUtils.mkdir_p("#{@base_dir}/screencast_recordings/#{timestamp}/").first @recordings_dir end From 850cbaa1995fc0b1a75203011c2d57fc3f62bb92 Mon Sep 17 00:00:00 2001 From: Peter Singh Date: Sat, 4 Jan 2025 21:00:20 +0000 Subject: [PATCH 3/5] Remove debug logging --- lib/ferrum/page.rb | 1 - lib/ferrum/screencaster.rb | 4 ---- 2 files changed, 5 deletions(-) diff --git a/lib/ferrum/page.rb b/lib/ferrum/page.rb index 46f73d74..eb845a68 100644 --- a/lib/ferrum/page.rb +++ b/lib/ferrum/page.rb @@ -440,7 +440,6 @@ def subscribe on(:screencastFrame) do |params, _index, _total| @screencaster.add_frame(params) - warn "DEBUG: frame added in page.rb" end end diff --git a/lib/ferrum/screencaster.rb b/lib/ferrum/screencaster.rb index 3fbc801c..ef0848ab 100644 --- a/lib/ferrum/screencaster.rb +++ b/lib/ferrum/screencaster.rb @@ -14,8 +14,6 @@ def initialize(page) 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"])) } @@ -38,9 +36,7 @@ def start_screencast(base_dir = "") end def stop_screencast - warn "joining threads" @threads.each(&:join) - warn "stopped" @page.command("Page.stopScreencast") end end From 72b187b4fa30b50b4816313fd45dc0c0527e1196 Mon Sep 17 00:00:00 2001 From: Peter Singh Date: Wed, 8 Jan 2025 11:24:51 +0000 Subject: [PATCH 4/5] Apply suggestions from code review --- lib/ferrum/screencaster.rb | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/lib/ferrum/screencaster.rb b/lib/ferrum/screencaster.rb index ef0848ab..af1c2b78 100644 --- a/lib/ferrum/screencaster.rb +++ b/lib/ferrum/screencaster.rb @@ -10,28 +10,23 @@ def initialize(page) @page = page @frame_number = 0 @threads = [] - @base_dir = "" + @save_path end def add_frame(params) @page.command("Page.screencastFrameAck", sessionId: params["sessionId"]) - t = Thread.new { File.binwrite("#{recordings_dir}/frame-#{@frame_number}.jpeg", Base64.decode64(params["data"])) } + t = Thread.new { File.binwrite("#{@save_path}/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 - timestamp = (Time.now.to_f * 1000).to_i - @recordings_dir = FileUtils.mkdir_p("#{@base_dir}/screencast_recordings/#{timestamp}/").first - @recordings_dir - end - - def start_screencast(base_dir = "") - @base_dir = base_dir + # save_path: Directory where individual frames from the screencast will be saved + def start_screencast(save_path) + @save_path = save_path + raise "Save path for screen recording does not exist" unless Dir.exist?(@save_path) @page.command("Page.startScreencast", format: "jpeg") end From d9a2fadf9f19cc8be6cc087dc5c680e8a09cf537 Mon Sep 17 00:00:00 2001 From: Peter Singh Date: Wed, 8 Jan 2025 11:54:23 +0000 Subject: [PATCH 5/5] Update lib/ferrum/screencaster.rb --- lib/ferrum/screencaster.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ferrum/screencaster.rb b/lib/ferrum/screencaster.rb index af1c2b78..aad69bb3 100644 --- a/lib/ferrum/screencaster.rb +++ b/lib/ferrum/screencaster.rb @@ -2,7 +2,7 @@ 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 +# ffmpeg -y -framerate 30 -i 'frame-%d.jpeg' -c:v libx264 -r 30 -pix_fmt yuv420p output.mp4 # module Ferrum class Screencaster