;; 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})))