162 lines
6.5 KiB
Plaintext
162 lines
6.5 KiB
Plaintext
|
;; Scripts a real live chromium browser to take screenshots from ADS-B
|
||
|
;; Exchange.
|
||
|
|
||
|
(ns lemondronor.circlebot.adsbx
|
||
|
(:require
|
||
|
["fs" :as fs]
|
||
|
["puppeteer" :as puppeteer]
|
||
|
[clojure.string :as string]
|
||
|
[goog.string :as gstring]
|
||
|
[kitchen-async.promise :as p]
|
||
|
[lemondronor.circlebot.logging :as logging]
|
||
|
goog.string.format))
|
||
|
|
||
|
(logging/deflog "adsbx" logger)
|
||
|
|
||
|
(def user-agent "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/53 (KHTML, like Gecko) Chrome/15.0.87")
|
||
|
|
||
|
|
||
|
;; (defn wait-for-property [page prop-string]
|
||
|
;; (let [pieces (string/split prop-string ".")]
|
||
|
;; (p/loop [pieces-checking (take 1 pieces)
|
||
|
;; pieces-remaining (drop 1 pieces)
|
||
|
;; ]
|
||
|
;; (println "Waiting for" (string/join "." pieces-checking))
|
||
|
;; (p/resolve (.waitForFunction page (string/join "." pieces-checking)))
|
||
|
;; (when (seq pieces-remaining)
|
||
|
;; (recur (concat pieces-checking (take 1 pieces-remaining))
|
||
|
;; (drop 1 pieces-remaining))))))
|
||
|
|
||
|
|
||
|
(defn timeout
|
||
|
([ms]
|
||
|
(timeout ms nil))
|
||
|
([ms v]
|
||
|
(p/promise [resolve]
|
||
|
(js/setTimeout #(resolve v) ms))))
|
||
|
|
||
|
|
||
|
(defn current-time-secs []
|
||
|
(/ (.getTime (js/Date.)) 1000))
|
||
|
|
||
|
|
||
|
(defn import-settings [page settings]
|
||
|
(let [import-settings-btn-xpath "//button[contains(text(), 'Import Settings')]"]
|
||
|
(p/try
|
||
|
(log-debug "Importing settings")
|
||
|
(p/all [(.waitForNavigation page)
|
||
|
(.waitForXPath page import-settings-btn-xpath)
|
||
|
(.goto page
|
||
|
"https://global.adsbexchange.com/VirtualRadar/settings.html"
|
||
|
(clj->js {:referer "https://adsbexchange.com/"}))])
|
||
|
(timeout 50)
|
||
|
(p/let [import-settings-btn (.$x page import-settings-btn-xpath)]
|
||
|
(.click (nth import-settings-btn 0))
|
||
|
(.waitForSelector page "textarea")
|
||
|
(p/let [textarea (.$x page "//textarea")]
|
||
|
(.evaluate page
|
||
|
(fn [el value]
|
||
|
(set! (.-value el) value))
|
||
|
(nth textarea 0)
|
||
|
settings))
|
||
|
(p/let [import-btn (.$x page "//button[text()='Import']")]
|
||
|
(.click (nth import-btn 0))
|
||
|
(timeout 100)))
|
||
|
(log-debug "Imported settings"))))
|
||
|
|
||
|
|
||
|
(defn screenshot-aircraft
|
||
|
([icao lat lon]
|
||
|
(screenshot-aircraft icao lat lon {}))
|
||
|
([icao lat lon options]
|
||
|
(log-info "Taking screenshot of aircraft %s at %s %s" icao lat lon)
|
||
|
(let [icao-selector (gstring/format "//td[contains(., '%s')]"
|
||
|
(string/upper-case icao))
|
||
|
launch-options {:headless (get options :headless? true)
|
||
|
:defaultViewport (get options :viewport {:width 1600
|
||
|
:height 800})}
|
||
|
took-screenshot?_ (atom false)]
|
||
|
(p/race
|
||
|
[(p/promise [resolve reject]
|
||
|
(js/setTimeout
|
||
|
#(when-not @took-screenshot?_
|
||
|
(log-error "Screenshot timing out")
|
||
|
(reject (js/Error. "Timeout")))
|
||
|
(get options :timeout 30000)))
|
||
|
(p/let [browser (puppeteer/launch (clj->js launch-options))
|
||
|
page (.newPage browser)]
|
||
|
(if-let [user-agent (:user-agent options)]
|
||
|
(.setUserAgent page user-agent))
|
||
|
(if-let [vrs-settings (:vrs-settings options)]
|
||
|
(import-settings page vrs-settings))
|
||
|
(log-debug "Navigating")
|
||
|
(p/all [(.waitForNavigation page)
|
||
|
(.goto page
|
||
|
"https://global.adsbexchange.com/VirtualRadar/desktop.html"
|
||
|
(clj->js {:referer "https://adsbexchange.com/"}))])
|
||
|
;; Wait until the page has loaded the pan and zoom functions
|
||
|
;; we need.
|
||
|
(log-debug "Waiting for script objects")
|
||
|
(.waitForFunction page "window.VRS")
|
||
|
(.waitForFunction page "VRS.bootstrap")
|
||
|
(.waitForFunction page "VRS.bootstrap.pageSettings")
|
||
|
(.waitForFunction page "VRS.bootstrap.pageSettings.mapPlugin")
|
||
|
(log-debug "Xooming")
|
||
|
;; Zoom.
|
||
|
(.evaluate
|
||
|
page
|
||
|
(gstring/format "VRS.bootstrap.pageSettings.mapPlugin.setZoom(%s);" (get options :zoom 13)))
|
||
|
(timeout 500)
|
||
|
;; Pan to the coordinates of interest.
|
||
|
(log-debug "Panning")
|
||
|
(.evaluate
|
||
|
page
|
||
|
(gstring/format
|
||
|
"VRS.bootstrap.pageSettings.mapPlugin.panTo({lat:%f,lng:%f});"
|
||
|
lat
|
||
|
lon))
|
||
|
;; Once we pan to our area of interest it can take some time
|
||
|
;; for VRS to load in the map tiles and the aircraft present
|
||
|
;; in that area. So we wait before clicking.
|
||
|
(log-debug "Waiting for aircraft")
|
||
|
(.waitForXPath page icao-selector)
|
||
|
;; Now try to click on the aircraft of interest in the list
|
||
|
;; display.
|
||
|
(log-debug "Selecting aircraft")
|
||
|
(p/let [aircraft-entry (.$x page icao-selector)]
|
||
|
(.click (nth aircraft-entry 0)))
|
||
|
;; Click "Show on map" to center it.
|
||
|
(p/let [show-on-map (.$x page "//a[contains(., 'Show on map')]")]
|
||
|
(.click (nth show-on-map 0)))
|
||
|
;; Wait for tiles to load. I wonder if we could try something
|
||
|
;; like this:
|
||
|
;; https://stackoverflow.com/questions/22288933/how-to-tell-when-all-visible-tiles-have-fully-loaded
|
||
|
(timeout 3000)
|
||
|
(let [path (get options :output-path (str "screenshot-"
|
||
|
(.toFixed (current-time-secs) 0)
|
||
|
"-"
|
||
|
icao
|
||
|
".png"))]
|
||
|
(log-info "Writing %s" path)
|
||
|
(p/do
|
||
|
(.screenshot page (clj->js {:path path
|
||
|
:clip (get options :clip {:width 860
|
||
|
:height 680
|
||
|
:x 46
|
||
|
:y 46})}))
|
||
|
(log-info "screenshot done")
|
||
|
(reset! took-screenshot?_ true)
|
||
|
(log-info "closing browser")
|
||
|
(.close browser)
|
||
|
(log-info "closing browser done")
|
||
|
path)))]))))
|
||
|
|
||
|
|
||
|
;; (defn main [& args]
|
||
|
;; (let [settings (fs/readFileSync "vrs-settings.json" "utf-8")
|
||
|
;; zoom 13
|
||
|
;; icao (nth args 0)
|
||
|
;; lat (js/parseFloat (nth args 1))
|
||
|
;; lon (js/parseFloat (nth args 2))]
|
||
|
;; (screenshot-aircraft icao lat lon {:zoom zoom :vrs-settings settings})))
|