2021-02-15 12:15:16 +01:00
|
|
|
;; This Source Code Form is subject to the terms of the Mozilla Public
|
|
|
|
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
|
|
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
|
|
;;
|
2021-04-10 09:43:04 +02:00
|
|
|
;; Copyright (c) UXBOX Labs SL
|
2021-02-15 12:15:16 +01:00
|
|
|
|
2020-06-29 16:07:48 +02:00
|
|
|
(ns app.browser
|
|
|
|
(:require
|
2021-05-05 09:28:12 +02:00
|
|
|
[app.config :as cf]
|
2020-06-29 16:07:48 +02:00
|
|
|
[lambdaisland.glogi :as log]
|
|
|
|
[promesa.core :as p]
|
|
|
|
["puppeteer-cluster" :as ppc]))
|
|
|
|
|
2021-05-05 09:28:12 +02:00
|
|
|
;; --- BROWSER API
|
|
|
|
|
2020-06-29 16:07:48 +02:00
|
|
|
(def USER-AGENT
|
|
|
|
(str "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 "
|
|
|
|
"(KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36"))
|
|
|
|
|
|
|
|
(defn exec!
|
|
|
|
[browser f]
|
|
|
|
(.execute ^js browser (fn [props]
|
|
|
|
(let [page (unchecked-get props "page")]
|
|
|
|
(f page)))))
|
|
|
|
|
|
|
|
(defn emulate!
|
2020-07-02 14:48:17 +02:00
|
|
|
[page {:keys [viewport user-agent scale]
|
|
|
|
:or {user-agent USER-AGENT
|
|
|
|
scale 1}}]
|
2020-06-29 16:07:48 +02:00
|
|
|
(let [[width height] viewport]
|
2020-07-30 15:23:11 +02:00
|
|
|
(.emulate ^js page #js {:viewport #js {:width width
|
|
|
|
:height height
|
|
|
|
:deviceScaleFactor scale}
|
|
|
|
:userAgent user-agent})))
|
2020-06-29 16:07:48 +02:00
|
|
|
|
|
|
|
(defn navigate!
|
|
|
|
([page url] (navigate! page url nil))
|
|
|
|
([page url {:keys [wait-until]
|
|
|
|
:or {wait-until "networkidle2"}}]
|
|
|
|
(.goto ^js page url #js {:waitUntil wait-until})))
|
|
|
|
|
|
|
|
(defn sleep
|
|
|
|
[page ms]
|
2021-03-02 10:51:06 +01:00
|
|
|
(.waitForTimeout ^js page ms))
|
|
|
|
|
|
|
|
|
|
|
|
(defn wait-for
|
|
|
|
([page selector] (wait-for page selector nil))
|
|
|
|
([page selector {:keys [visible] :or {visible false}}]
|
|
|
|
(.waitForSelector ^js page selector #js {:visible visible})))
|
2020-06-29 16:07:48 +02:00
|
|
|
|
|
|
|
(defn screenshot
|
2020-07-02 14:48:17 +02:00
|
|
|
([frame] (screenshot frame nil))
|
2020-07-30 15:23:11 +02:00
|
|
|
([frame {:keys [full-page? omit-background? type]
|
2020-07-02 14:48:17 +02:00
|
|
|
:or {full-page? false
|
2020-07-30 15:23:11 +02:00
|
|
|
type "png"
|
2020-07-02 14:48:17 +02:00
|
|
|
omit-background? false}}]
|
|
|
|
(.screenshot ^js frame #js {:fullPage full-page?
|
2020-07-30 15:23:11 +02:00
|
|
|
:type (name type)
|
2020-07-02 14:48:17 +02:00
|
|
|
:omitBackground omit-background?})))
|
|
|
|
|
|
|
|
(defn eval!
|
|
|
|
[frame f]
|
|
|
|
(.evaluate ^js frame f))
|
|
|
|
|
|
|
|
(defn select
|
|
|
|
[frame selector]
|
|
|
|
(.$ ^js frame selector))
|
2020-06-29 16:07:48 +02:00
|
|
|
|
2020-07-30 15:23:11 +02:00
|
|
|
(defn select-all
|
|
|
|
[frame selector]
|
|
|
|
(.$$ ^js frame selector))
|
|
|
|
|
2020-06-29 16:07:48 +02:00
|
|
|
(defn set-cookie!
|
|
|
|
[page {:keys [key value domain]}]
|
|
|
|
(.setCookie ^js page #js {:name key
|
|
|
|
:value value
|
|
|
|
:domain domain}))
|
|
|
|
|
2021-05-05 09:28:12 +02:00
|
|
|
;; --- BROWSER STATE
|
|
|
|
|
|
|
|
(def instance (atom nil))
|
|
|
|
|
|
|
|
(defn- create-browser
|
|
|
|
[concurrency strategy]
|
|
|
|
(let [strategy (case strategy
|
|
|
|
:browser (.-CONCURRENCY_BROWSER ^js ppc/Cluster)
|
|
|
|
:incognito (.-CONCURRENCY_CONTEXT ^js ppc/Cluster)
|
|
|
|
:page (.-CONCURRENCY_PAGE ^js ppc/Cluster))
|
|
|
|
opts #js {:concurrency strategy
|
|
|
|
:maxConcurrency concurrency
|
|
|
|
:puppeteerOptions #js {:args #js ["--no-sandbox"]}}]
|
|
|
|
(.launch ^js ppc/Cluster opts)))
|
|
|
|
|
|
|
|
|
|
|
|
(defn init
|
|
|
|
[]
|
|
|
|
(let [concurrency (cf/get :browser-concurrency)
|
|
|
|
strategy (cf/get :browser-strategy)]
|
|
|
|
(-> (create-browser concurrency strategy)
|
|
|
|
(p/then #(reset! instance %))
|
|
|
|
(p/catch (fn [error]
|
|
|
|
(log/error :msg "failed to initialize browser")
|
|
|
|
(js/console.error error))))))
|
|
|
|
|
|
|
|
|
|
|
|
(defn stop
|
|
|
|
[]
|
|
|
|
(if-let [instance @instance]
|
|
|
|
(p/do!
|
|
|
|
(.idle ^js instance)
|
|
|
|
(.close ^js instance)
|
|
|
|
(log/info :msg "shutdown headless browser"))
|
|
|
|
(p/resolved nil)))
|