adspower-client 1.0.18 → 1.0.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f42b0809ed76da37b7806a4f70ce727efe871def40a4a90c5810d972b0ea0556
4
- data.tar.gz: d22f49494cae5e3ae3195984c604eea018c6dc180cb1b9dd3d62fcab55f21851
3
+ metadata.gz: d0ccba83af94aa8269d8f9900dc833c8272dae8d157488f5e8f6f5e6b0271655
4
+ data.tar.gz: 26eb3e2830ba0fdac9c393baf0b1bd5ccb68ee06ee23107b63d45beb1bbcbc62
5
5
  SHA512:
6
- metadata.gz: e5951622b5d9f01c5515df6ce671cd0afd6a20872e6dedab34be1d088ebef3f6811b9473a700221e7447e3e6d2d811fe149a6fa3e8cd917ae9abad84e5f8dc24
7
- data.tar.gz: 7c589af8831eb9596ab2799ae04bd9af6433883f467234e8fe220cd04a6bfc526e31aed74d4b5671330ab5034fc207cbb54c34fc5854f1ae291b431e6985de40
6
+ metadata.gz: c4012e0f78559bc6b729b926c104d3c16571a95624690243468757d93f4648df504149614901c26eb100f83255525db4859549e0fe06ef29754fd3df7f021c2b
7
+ data.tar.gz: 87085d7433b2eaf109ca6e2c467597add299c110d175950baa5786302372398eea303ed722af08e5d18aa6d280d7d2e32451739399784f1970dfe41686c84a2b
@@ -1,7 +1,7 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = 'adspower-client'
3
- s.version = '1.0.18'
4
- s.date = '2025-07-28'
3
+ s.version = '1.0.20'
4
+ s.date = '2025-12-10'
5
5
  s.summary = "Ruby library for operating AdsPower API."
6
6
  s.description = "Ruby library for operating AdsPower API."
7
7
  s.authors = ["Leandro Daniel Sardi"]
@@ -49,9 +49,23 @@ class AdsPowerClient
49
49
 
50
50
  # Release the lock
51
51
  def release_lock
52
- @lockfile.flock(File::LOCK_UN) if @lockfile
52
+ if @lockfile
53
+ @lockfile.flock(File::LOCK_UN)
54
+ # don't close while you expect further operations; close only on object shutdown
55
+ # @lockfile.close
56
+ end
53
57
  end
54
-
58
+ =begin
59
+ # add a destructor to close file when object is GC'd / program shuts down
60
+ # Call client.close when you're done with the client instance (e.g., at program exit).
61
+ def close
62
+ if @lockfile && !@lockfile.closed?
63
+ @lockfile.flock(File::LOCK_UN) rescue nil
64
+ @lockfile.close rescue nil
65
+ @lockfile = nil
66
+ end
67
+ end
68
+ =end
55
69
  # Wrapper method for critical sections
56
70
  def with_lock
57
71
  acquire_lock
@@ -81,12 +95,67 @@ class AdsPowerClient
81
95
  return
82
96
  end
83
97
 
98
+ def self.drivers
99
+ @@drivers
100
+ end
101
+
102
+ # Quit and remove all drivers (safe)
103
+ def self.cleanup_all
104
+ @@drivers.keys.each do |id|
105
+ drv = @@drivers[id]
106
+ begin
107
+ drv.quit if drv
108
+ rescue => e
109
+ # best-effort: ignore but log if desired
110
+ ensure
111
+ @@drivers.delete(id)
112
+ end
113
+ end
114
+ end
115
+
116
+ # Quit and remove a specific driver (safe)
117
+ def self.cleanup(id)
118
+ drv = @@drivers[id]
119
+ begin
120
+ drv.quit if drv
121
+ rescue => e
122
+ # best-effort: ignore but log if desired
123
+ ensure
124
+ @@drivers.delete(id)
125
+ end
126
+ end
127
+ =begin
128
+ # Try to kill processes matching a pattern (last resort)
129
+ # Call it only if quit and client.stop both failed.
130
+ def self.force_kill_by_ws(ws_endpoint)
131
+ # example ws_endpoint: "127.0.0.1:12345" -> 12345
132
+ port = ws_endpoint.split(':').last rescue nil
133
+ return unless port
134
+ # find and kill processes that hold that port
135
+ begin
136
+ pids = `lsof -ti tcp:#{port}`.split.map(&:to_i)
137
+ pids.each { |pid| Process.kill('KILL', pid) rescue nil }
138
+ rescue => e
139
+ # ignore/ log
140
+ end
141
+ end
142
+ =end
84
143
  # Kill all the adspower_global processes running on the local computer.
85
144
  def server_stop
86
145
  with_lock do
87
146
  self.server_pids.each { |pid|
88
147
  `kill -9 #{pid}`
89
148
  }
149
+ # clean up @@driver
150
+ @@drivers.each do |id, driver|
151
+ begin
152
+ driver.quit if driver
153
+ rescue => e
154
+ # Log or handle the exception if needed
155
+ ensure
156
+ @@drivers[id] = nil
157
+ end
158
+ end
90
159
  end
91
160
  return
92
161
  end
@@ -247,13 +316,14 @@ class AdsPowerClient
247
316
  proxy_config:,
248
317
  group_id: '0',
249
318
  browser_version: nil,
250
- os: 'linux64',# new: one of linux64, mac-x64, mac-arm64, win32, win64
319
+ os: 'linux64', # new: one of linux64, mac-x64, mac-arm64, win32, win64
251
320
  fingerprint: nil,
252
- platform: '', # default: no platform
253
- tabs: [], # default: no tabs to open
254
- username: '', # default: no login
255
- password: '', # default: no password
256
- fakey: '' # leave blank if no 2FA
321
+ platform: '', # default: no platform
322
+ tabs: [], # default: no tabs to open
323
+ username: '', # default: no login
324
+ password: '', # default: no password
325
+ fakey: '', # leave blank if no 2FA
326
+ cookie: nil # import cookies - Type: Text - Format: JSON - username/password are ignored if cookie is not nil.
257
327
  )
258
328
  browser_version ||= adspower_default_browser_version
259
329
 
@@ -313,6 +383,7 @@ class AdsPowerClient
313
383
  'username' => username,
314
384
  'password' => password,
315
385
  'fakey' => fakey, # 2FA, if any
386
+ 'cookie' => cookie,
316
387
 
317
388
  # ─── FINGERPRINT ──────────────────────────────────
318
389
  "fingerprint_config" => fingerprint || {
@@ -478,8 +549,25 @@ class AdsPowerClient
478
549
  end
479
550
 
480
551
  def driver2(id, headless: false, read_timeout: 180)
481
- return @@drivers[id] if @@drivers[id]
482
-
552
+ #return @@drivers[id] if @@drivers[id]
553
+ # If we have a cached driver, verify it's still valid
554
+ if @@drivers[id]
555
+ begin
556
+ # quick, non-destructive sanity check: ask for window_handles
557
+ @@drivers[id].window_handles
558
+ return @@drivers[id]
559
+ rescue Selenium::WebDriver::Error::InvalidSessionIdError,
560
+ Selenium::WebDriver::Error::NoSuchWindowError,
561
+ Errno::ECONNREFUSED => e
562
+ # stale/broken driver: best-effort cleanup and continue to create a new one
563
+ #warn "detected stale driver for #{id}: #{e.class}: #{e.message}"
564
+ self.class.cleanup(id)
565
+ rescue => e
566
+ #warn "unexpected error checking cached driver: #{e.class}: #{e.message}"
567
+ self.class.cleanup(id)
568
+ end
569
+ end
570
+
483
571
  # 1) start the AdsPower profile / grab its WebSocket URL
484
572
  data = start(id, headless)['data']
485
573
  ws = data['ws']['selenium'] # e.g. "127.0.0.1:XXXXX"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: adspower-client
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.18
4
+ version: 1.0.20
5
5
  platform: ruby
6
6
  authors:
7
7
  - Leandro Daniel Sardi
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-07-28 00:00:00.000000000 Z
11
+ date: 2025-12-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: uri