Class: AdsPowerClient
- Inherits:
-
Object
- Object
- AdsPowerClient
- Defined in:
- lib/adspower-client.rb
Constant Summary collapse
- CLOUD_API_BASE =
'https://api.adspower.com/v1'
- COUNTRY_LANG =
Constante generada en tiempo de ejecución:
ISO3166::Country.all.each_with_object({}) do |country, h| # El primer idioma oficial (ISO 639-1) que encuentre: language_code = country.languages&.first || 'en' # Construimos la etiqueta BCP47 Language-Region: h[country.alpha2] = "#{language_code}-#{country.alpha2}" end.freeze
- LOCK_FILE =
'/tmp/adspower_api_lock'
- @@drivers =
control over the drivers created, in order to not create the same driver twice and not generate memory leaks. reference: github.com/leandrosardi/adspower-client/issues/4
{}
Instance Attribute Summary collapse
-
#adspower_default_browser_version ⇒ Object
reference: localapi-doc-en.adspower.com/ reference: localapi-doc-en.adspower.com/docs/Rdw7Iu.
-
#adspower_listener ⇒ Object
reference: localapi-doc-en.adspower.com/ reference: localapi-doc-en.adspower.com/docs/Rdw7Iu.
-
#cloud_token ⇒ Object
reference: localapi-doc-en.adspower.com/ reference: localapi-doc-en.adspower.com/docs/Rdw7Iu.
-
#key ⇒ Object
reference: localapi-doc-en.adspower.com/ reference: localapi-doc-en.adspower.com/docs/Rdw7Iu.
-
#port ⇒ Object
reference: localapi-doc-en.adspower.com/ reference: localapi-doc-en.adspower.com/docs/Rdw7Iu.
-
#server_log ⇒ Object
reference: localapi-doc-en.adspower.com/ reference: localapi-doc-en.adspower.com/docs/Rdw7Iu.
Instance Method Summary collapse
-
#acquire_lock ⇒ Object
Acquire the lock.
-
#check(id) ⇒ Object
Check if the browser session for the given user profile is active.
-
#cloud_profile_quota ⇒ Object
Return a hash with: • :limit ⇒ total profile slots allowed (-1 = unlimited) • :used ⇒ number of profiles currently created • :remaining ⇒ slots left (nil if unlimited) Fetch your real profile quota from the Cloud API.
-
#create ⇒ Object
Create a new user profile via API call and return the ID of the created user.
-
#create2(name:, proxy_config:, group_id: '0', browser_version: nil, os: 'linux64', fingerprint: nil, platform: '', tabs: [], username: '', password: '', fakey: '') ⇒ Object
Create a new desktop profile with: • name, proxy, fingerprint, etc (unchanged) • platform (e.g. “linkedin.com”) • tabs (Array of URLs to open) • username / password / fakey for that platform.
-
#delete(id) ⇒ Object
Delete a user profile via API call.
-
#driver(id, headless = false) ⇒ Object
Attach to the existing browser session with Selenium WebDriver.
- #driver2(id, headless: false, read_timeout: 180) ⇒ Object
-
#find_group_id_by_name(name) ⇒ Object
Find the numeric ID for a given group name (exact match).
-
#geolocate(ip) ⇒ Object
Lookup GeoIP gratuito (freegeoip.app) y parseo básico.
-
#html(url) ⇒ Object
DEPRECATED - Use Zyte instead of this method.
-
#initialize(h = {}) ⇒ AdsPowerClient
constructor
A new instance of AdsPowerClient.
-
#list_groups ⇒ Object
Fetch all groups.
-
#list_profiles(group_id: nil) ⇒ Object
returns an Array of profile‑hashes from the local API.
-
#online? ⇒ Boolean
Send a GET request to “#url/status” and return true if it responded successfully.
-
#profile_count(group_id: nil) ⇒ Object
Count current profiles (optionally filtered by group).
-
#release_lock ⇒ Object
Release the lock.
-
#server_pids ⇒ Object
Return an array of PIDs of all the adspower_global processes running on the local computer.
-
#server_start(timeout = 30) ⇒ Object
Run async command to start AdsPower server in headless mode.
-
#server_stop ⇒ Object
Kill all the adspower_global processes running on the local computer.
-
#start(id, headless = false) ⇒ Object
Start the browser with the given user profile and return the connection details.
-
#stop(id) ⇒ Object
Stop the browser session for the given user profile.
-
#with_lock ⇒ Object
Wrapper method for critical sections.
Constructor Details
#initialize(h = {}) ⇒ AdsPowerClient
Returns a new instance of AdsPowerClient.
31 32 33 34 35 36 37 38 39 40 41 42 |
# File 'lib/adspower-client.rb', line 31 def initialize(h={}) self.key = h[:key] # mandatory self.port = h[:port] || '50325' self.server_log = h[:server_log] || '~/adspower-client.log' self.adspower_listener = h[:adspower_listener] || 'http://127.0.0.1' # DEPRECATED self.adspower_default_browser_version = h[:adspower_default_browser_version] || '116' # PENDING self.cloud_token = h[:cloud_token] end |
Instance Attribute Details
#adspower_default_browser_version ⇒ Object
reference: localapi-doc-en.adspower.com/ reference: localapi-doc-en.adspower.com/docs/Rdw7Iu
23 24 25 |
# File 'lib/adspower-client.rb', line 23 def adspower_default_browser_version @adspower_default_browser_version end |
#adspower_listener ⇒ Object
reference: localapi-doc-en.adspower.com/ reference: localapi-doc-en.adspower.com/docs/Rdw7Iu
23 24 25 |
# File 'lib/adspower-client.rb', line 23 def adspower_listener @adspower_listener end |
#cloud_token ⇒ Object
reference: localapi-doc-en.adspower.com/ reference: localapi-doc-en.adspower.com/docs/Rdw7Iu
23 24 25 |
# File 'lib/adspower-client.rb', line 23 def cloud_token @cloud_token end |
#key ⇒ Object
reference: localapi-doc-en.adspower.com/ reference: localapi-doc-en.adspower.com/docs/Rdw7Iu
23 24 25 |
# File 'lib/adspower-client.rb', line 23 def key @key end |
#port ⇒ Object
reference: localapi-doc-en.adspower.com/ reference: localapi-doc-en.adspower.com/docs/Rdw7Iu
23 24 25 |
# File 'lib/adspower-client.rb', line 23 def port @port end |
#server_log ⇒ Object
reference: localapi-doc-en.adspower.com/ reference: localapi-doc-en.adspower.com/docs/Rdw7Iu
23 24 25 |
# File 'lib/adspower-client.rb', line 23 def server_log @server_log end |
Instance Method Details
#acquire_lock ⇒ Object
Acquire the lock
45 46 47 48 |
# File 'lib/adspower-client.rb', line 45 def acquire_lock @lockfile ||= File.open(LOCK_FILE, File::CREAT | File::RDWR) @lockfile.flock(File::LOCK_EX) end |
#check(id) ⇒ Object
Check if the browser session for the given user profile is active.
446 447 448 449 450 451 452 453 454 |
# File 'lib/adspower-client.rb', line 446 def check(id) with_lock do url = "#{self.adspower_listener}:#{port}/api/v1/browser/active?user_id=#{id}" uri = URI.parse(url) res = Net::HTTP.get(uri) return false if JSON.parse(res)['msg'] != 'success' JSON.parse(res)['data']['status'] == 'Active' end end |
#cloud_profile_quota ⇒ Object
Return a hash with:
• :limit ⇒ total profile slots allowed (-1 = unlimited)
• :used ⇒ number of profiles currently created
• :remaining ⇒ slots left (nil if unlimited)
Fetch your real profile quota from the Cloud API
171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 |
# File 'lib/adspower-client.rb', line 171 def cloud_profile_quota uri = URI("#{CLOUD_API_BASE}/account/get_info") req = Net::HTTP::Get.new(uri) req['Authorization'] = "Bearer #{self.cloud_token}" res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http| http.request(req) end data = JSON.parse(res.body) raise "Cloud API error: #{data['msg']}" unless data['code'] == 0 allowed = data['data']['total_profiles_allowed'].to_i used = data['data']['profiles_used'].to_i remaining = allowed < 0 ? nil : (allowed - used) { limit: allowed, used: used, remaining: remaining } end |
#create ⇒ Object
Create a new user profile via API call and return the ID of the created user.
192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 |
# File 'lib/adspower-client.rb', line 192 def create with_lock do url = "#{self.adspower_listener}:#{port}/api/v1/user/create" body = { 'group_id' => '0', 'proxyid' => '1', 'fingerprint_config' => { 'browser_kernel_config' => {"version": self.adspower_default_browser_version, "type": "chrome"} } } # API call res = BlackStack::Netting.call_post(url, body) ret = JSON.parse(res.body) raise "Error: #{ret.to_s}" if ret['msg'].to_s.downcase != 'success' ret['data']['id'] end end |
#create2(name:, proxy_config:, group_id: '0', browser_version: nil, os: 'linux64', fingerprint: nil, platform: '', tabs: [], username: '', password: '', fakey: '') ⇒ Object
Create a new desktop profile with:
• name, proxy, fingerprint, etc (unchanged)
• platform (e.g. "linkedin.com")
• tabs (Array of URLs to open)
• username / password / fakey for that platform
245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 |
# File 'lib/adspower-client.rb', line 245 def create2( name:, proxy_config:, group_id: '0', browser_version: nil, os: 'linux64',# new: one of linux64, mac-x64, mac-arm64, win32, win64 fingerprint: nil, platform: '', # default: no platform tabs: [], # default: no tabs to open username: '', # default: no login password: '', # default: no password fakey: '' # leave blank if no 2FA ) browser_version ||= adspower_default_browser_version # 0) Resolve full Chrome version ───────────────────────────── # Fetch the list of known-good Chrome versions and pick the highest uri = URI('https://googlechromelabs.github.io/chrome-for-testing/known-good-versions-with-downloads.json') resp = Net::HTTP.get_response(uri) unless resp.is_a?(Net::HTTPSuccess) raise "Error fetching Chrome versions: HTTP #{resp.code}" end listing = JSON.parse(resp.body) entries = listing['versions'] || [] # keep only those entries whose version matches prefix *and* has a download for our OS matches = entries. select { |e| e['version'].start_with?("#{browser_version}.") && e.dig('downloads','chrome').any? { |d| d['platform'] == os } }. map { |e| e['version'] } if matches.empty? raise "Chrome version '#{browser_version}' not found in known-good versions list" end # pick the highest patch/build by semantic compare full_version = matches .map { |ver| ver.split('.').map(&:to_i) } .max .join('.') # 1) Hacemos GeoIP sobre la IP del proxy geo = geolocate(proxy_config[:ip]) lang = COUNTRY_LANG[geo[:country_code]] || "en-US" screen_res = "1920_1080" with_lock do url = "#{adspower_listener}:#{port}/api/v2/browser-profile/create" body = { # ─── GENERAL & PROXY ───────────────────────────── 'name' => name, 'group_id' => group_id, 'user_proxy_config' => { 'proxy_soft' => proxy_config[:proxy_soft] || 'other', 'proxy_type' => proxy_config[:proxy_type] || 'socks5', 'proxy_host' => proxy_config[:ip], 'proxy_port' => proxy_config[:port].to_s, 'proxy_user' => proxy_config[:user], 'proxy_password' => proxy_config[:password], # ─── FORCE ALL DNS THROUGH PROXY ───────────────── # Avoid DNS-Leak "proxy_dns": 1, # 1 = yes, 0 = no "dns_servers": ["8.8.8.8","8.8.4.4"] # optional: your choice of DNS }, # ─── PLATFORM ───────────────────────────────────── 'platform' => platform, # must be one of AdsPower’s supported “sites” 'tabs' => tabs, # array of URLs to open 'username' => username, 'password' => password, 'fakey' => fakey, # 2FA, if any # ─── FINGERPRINT ────────────────────────────────── "fingerprint_config" => fingerprint || { # ─── 0) DNS Leak Prevention ─────────────────────────── # Even with “proxy_dns” forced on, a few ISPs will still # silently intercept every UDP:53 out of your AdsPower VPS # and shove it into their own resolver farm (the classic # “transparent DNS proxy” attack that BrowserScan is warning you about). # # Because you refuse to hot-patch your Chrome via extra args or CDP, # the only way to survive an ISP-level hijack is to push all name lookups # into an encrypted channel that the ISP simply can’t touch: DNS-over-HTTPS (DoH). # # Here’s the minimal change you need to bake into your AdsPower profile at # creation time so that every DNS query happens inside Chrome’s DoH stack: # "extra_launch_flags" => [ # === DNS over HTTPS only === "--enable-features=DnsOverHttps", "--dns-over-https-mode=secure", "--dns-over-https-templates=https://cloudflare-dns.com/dns-query", "--disable-ipv6", # === hide “Chrome is being controlled…” banner === # # Even though you baked in the DoH flags under extra_launch_flags, # you never told Chrome to hide its “automation” banners or black-hole # all other DNS lookups — and BrowserScan still sees those UDP:53 calls # leaking out. # # What you need is to push three more flags into your profile creation, # and then attach with the exact same flags when Selenium hooks in. # "--disable-blink-features=AutomationControlled", "--disable-infobars", "--disable-features=TranslateUI", # optional but reduces tell-tale infobars "--host-resolver-rules=MAP * 0.0.0.0,EXCLUDE localhost,EXCLUDE cloudflare-dns.com" ], # ─── 1) Kernel & versión ─────────────────────────── "browser_kernel_config" => { "version" => browser_version, # aquí usamos el parámetro "type" => "chrome" }, # ─── 2) Timezone & locale ────────────────────────── "automatic_timezone" => "1", #"timezone" => geo[:time_zone], "language" => [ lang ], # ─── 3) User-Agent coherente ─────────────────────── "ua_category" => "desktop", 'ua' => "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/#{full_version} Safari/537.36", "is_mobile" => false, # ─── 4) Pantalla y plataforma ────────────────────── # It turns out that “Based on User-Agent” is purely a UI setting #"screen_resolution" => screen_res, "1920_1080" "platform" => "Linux x86_64", # ─── 5) Canvas & WebGL custom ───────────────────── "canvas" => "1", "webgl_image" => "1", "webgl" => "0", # 0=deshabilitado, 2=modo custom, 3=modo random-match "webgl_config" => { "unmasked_vendor" => "Intel Inc.", "unmasked_renderer" => "ANGLE (Intel, Mesa Intel(R) Xe Graphics (TGL GT2), OpenGL 4.6)", "webgpu" => { "webgpu_switch" => "1" } }, # ─── 6) Resto de ajustes ─────────────────────────── "webrtc" => "disabled", # WebRTC sí admite “disabled” "flash" => "block", # Flash únicamente “allow” o “block” "fonts" => [] # usar fonts por defecto } } res = BlackStack::Netting.call_post(url, body) ret = JSON.parse(res.body) raise "Error creating profile: #{ret['msg']}" unless ret['code'] == 0 ret['data']['profile_id'] end end |
#delete(id) ⇒ Object
Delete a user profile via API call.
403 404 405 406 407 408 409 410 411 412 413 414 415 |
# File 'lib/adspower-client.rb', line 403 def delete(id) with_lock do url = "#{self.adspower_listener}:#{port}/api/v1/user/delete" body = { 'api_key' => self.key, 'user_ids' => [id], } # API call res = BlackStack::Netting.call_post(url, body) ret = JSON.parse(res.body) raise "Error: #{ret.to_s}" if ret['msg'].to_s.downcase != 'success' end end |
#driver(id, headless = false) ⇒ Object
Attach to the existing browser session with Selenium WebDriver.
457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 |
# File 'lib/adspower-client.rb', line 457 def driver(id, headless=false) # Return the existing driver if it's still active. old = @@drivers[id] return old if old # Otherwise, start the driver ret = self.start(id, headless) # Attach test execution to the existing browser url = ret['data']['ws']['selenium'] opts = Selenium::WebDriver::Chrome::Options.new opts.add_option("debuggerAddress", url) # Connect to the existing browser driver = Selenium::WebDriver.for(:chrome, options: opts) # Save the driver @@drivers[id] = driver # Return the driver driver end |
#driver2(id, headless: false, read_timeout: 180) ⇒ Object
480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 |
# File 'lib/adspower-client.rb', line 480 def driver2(id, headless: false, read_timeout: 180) return @@drivers[id] if @@drivers[id] # 1) start the AdsPower profile / grab its WebSocket URL data = start(id, headless)['data'] ws = data['ws']['selenium'] # e.g. "127.0.0.1:XXXXX" # 2) attach with DevTools (no more excludeSwitches or caps!) opts = Selenium::WebDriver::Chrome::Options.new opts.debugger_address = ws opts.add_argument('--headless') if headless http = Selenium::WebDriver::Remote::Http::Default.new http.read_timeout = read_timeout driver = Selenium::WebDriver.for(:chrome, options: opts, http_client: http) driver.execute_cdp( 'Page.addScriptToEvaluateOnNewDocument', source: <<~JS // 1) remove any leftover cdc_… / webdriver hooks for (const k of Object.getOwnPropertyNames(window)) { if (k.startsWith('cdc_') || k.includes('webdriver')) { try { delete window[k]; } catch(e){} } } // 2) stub out window.chrome so Chrome-based detection thinks this is “normal” Chrome window.chrome = { runtime: {} }; JS ) @@drivers[id] = driver driver end |
#find_group_id_by_name(name) ⇒ Object
Find the numeric ID for a given group name (exact match)
118 119 120 121 |
# File 'lib/adspower-client.rb', line 118 def find_group_id_by_name(name) grp = list_groups.find { |g| g['group_name'].casecmp(name).zero? } grp ? grp['group_id'] : nil end |
#geolocate(ip) ⇒ Object
Lookup GeoIP gratuito (freegeoip.app) y parseo básico
212 213 214 215 216 217 218 219 220 221 222 223 224 225 |
# File 'lib/adspower-client.rb', line 212 def geolocate(ip) uri = URI("https://freegeoip.app/json/#{ip}") res = Net::HTTP.get(uri) h = JSON.parse(res) { country_code: h["country_code"], time_zone: h["time_zone"], latitude: h["latitude"], longitude: h["longitude"] } rescue # Fallback genérico { country_code: "US", time_zone: "America/New_York", latitude: 38.9, longitude: -77.0 } end |
#html(url) ⇒ Object
DEPRECATED - Use Zyte instead of this method.
Create a new profile, start the browser, visit a page, grab the HTML, and clean up.
522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 |
# File 'lib/adspower-client.rb', line 522 def html(url) ret = { :profile_id => nil, :html => nil, :status => 'success', } id = nil html = nil begin # Create the profile sleep(1) id = self.create # Update the result ret[:profile_id] = id # Start the profile and attach the driver driver = self.driver(id) # Get HTML driver.get(url) html = driver.page_source # Update the result ret[:html] = html # Stop the profile sleep(1) driver.quit self.stop(id) # Delete the profile sleep(1) self.delete(id) # Reset ID id = nil rescue => e # Stop and delete current profile if an error occurs if id sleep(1) self.stop(id) sleep(1) driver.quit if driver self.delete(id) if id end # Inform the exception ret[:status] = e.to_s # # process interruption # rescue SignalException, SystemExit, Interrupt => e # if id # sleep(1) # Avoid the "Too many request per second" error # self.stop(id) # sleep(1) # Avoid the "Too many request per second" error # driver.quit # self.delete(id) if id # end # if id end # Return ret end |
#list_groups ⇒ Object
Fetch all groups
109 110 111 112 113 114 115 |
# File 'lib/adspower-client.rb', line 109 def list_groups uri = URI("#{adspower_listener}:#{port}/api/v1/group/list?api_key=#{key}") resp = Net::HTTP.get(uri) data = JSON.parse(resp) raise "Error listing groups: #{data['msg']}" unless data['code'] == 0 data['data']['list'] # => [{ "id" => 0, "name" => "Ungrouped" }, …] end |
#list_profiles(group_id: nil) ⇒ Object
returns an Array of profile‑hashes from the local API
124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 |
# File 'lib/adspower-client.rb', line 124 def list_profiles(group_id: nil) all = [] page = 1 loop do params = { page: page, limit: 100 } params[:group_id] = group_id if group_id url = "#{adspower_listener}:#{port}/api/v2/browser-profile/list" resp = BlackStack::Netting.call_post(url, params) data = JSON.parse(resp.body) break unless data['code'] == 0 && data.dig('data','list').any? batch = data['data']['list'] all += batch page += 1 end all end |
#online? ⇒ Boolean
Send a GET request to “#url/status” and return true if it responded successfully.
95 96 97 98 99 100 101 102 103 104 105 106 |
# File 'lib/adspower-client.rb', line 95 def online? with_lock do begin url = "#{self.adspower_listener}:#{port}/status" uri = URI.parse(url) res = Net::HTTP.get(uri) return JSON.parse(res)['msg'] == 'success' rescue => e return false end end end |
#profile_count(group_id: nil) ⇒ Object
Count current profiles (optionally filtered by group)
144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 |
# File 'lib/adspower-client.rb', line 144 def profile_count(group_id: nil) count = 0 page = 1 loop do params = { page: page, limit: 100 } params[:group_id] = group_id if group_id url = "#{adspower_listener}:#{port}/api/v2/browser-profile/list" res = BlackStack::Netting.call_post(url, params) data = JSON.parse(res.body) raise "Error listing profiles: #{data['msg']}" unless data['code'] == 0 list = data['data']['list'] count += list.size break if list.size < 100 page += 1 end count end |
#release_lock ⇒ Object
Release the lock
51 52 53 |
# File 'lib/adspower-client.rb', line 51 def release_lock @lockfile.flock(File::LOCK_UN) if @lockfile end |
#server_pids ⇒ Object
Return an array of PIDs of all the adspower_global processes running on the local computer.
64 65 66 |
# File 'lib/adspower-client.rb', line 64 def server_pids `ps aux | grep "adspower_global" | grep -v grep | awk '{print $2}'`.split("\n") end |
#server_start(timeout = 30) ⇒ Object
Run async command to start AdsPower server in headless mode. Wait up to 10 seconds to start the server, or raise an exception.
70 71 72 73 74 75 76 77 78 79 80 81 82 |
# File 'lib/adspower-client.rb', line 70 def server_start(timeout=30) `xvfb-run --auto-servernum --server-args='-screen 0 1024x768x24' /usr/bin/adspower_global --headless=true --api-key=#{self.key.to_s} --api-port=#{self.port.to_s} > #{self.server_log} 2>&1 &` # wait up to 10 seconds to start the server timeout.times do break if self.online? sleep(1) end # add a delay of 5 more seconds sleep(5) # raise an exception if the server is not running raise "Error: the server is not running" if self.online? == false return end |
#server_stop ⇒ Object
Kill all the adspower_global processes running on the local computer.
85 86 87 88 89 90 91 92 |
# File 'lib/adspower-client.rb', line 85 def server_stop with_lock do self.server_pids.each { |pid| `kill -9 #{pid}` } end return end |
#start(id, headless = false) ⇒ Object
Start the browser with the given user profile and return the connection details.
418 419 420 421 422 423 424 425 426 427 |
# File 'lib/adspower-client.rb', line 418 def start(id, headless=false) with_lock do url = "#{self.adspower_listener}:#{port}/api/v1/browser/start?user_id=#{id}&headless=#{headless ? '1' : '0'}" uri = URI.parse(url) res = Net::HTTP.get(uri) ret = JSON.parse(res) raise "Error: #{ret.to_s}" if ret['msg'].to_s.downcase != 'success' ret end end |
#stop(id) ⇒ Object
Stop the browser session for the given user profile.
430 431 432 433 434 435 436 437 438 439 440 441 442 443 |
# File 'lib/adspower-client.rb', line 430 def stop(id) with_lock do if @@drivers[id] && self.check(id) @@drivers[id].quit @@drivers[id] = nil end uri = URI.parse("#{self.adspower_listener}:#{port}/api/v1/browser/stop?user_id=#{id}") res = Net::HTTP.get(uri) ret = JSON.parse(res) raise "Error: #{ret.to_s}" if ret['msg'].to_s.downcase != 'success' ret end end |
#with_lock ⇒ Object
Wrapper method for critical sections
56 57 58 59 60 61 |
# File 'lib/adspower-client.rb', line 56 def with_lock acquire_lock yield ensure release_lock end |