adspower-client 1.0.18 → 1.0.19

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: 6c91e849c43700d337b826208f705deef1bb3ec6e493ac872cd7d838afc67859
4
+ data.tar.gz: 176aefa58b9bd965fcbab16b7714dec6ac5cf3a8e0cad23b759248ae82b6fbd3
5
5
  SHA512:
6
- metadata.gz: e5951622b5d9f01c5515df6ce671cd0afd6a20872e6dedab34be1d088ebef3f6811b9473a700221e7447e3e6d2d811fe149a6fa3e8cd917ae9abad84e5f8dc24
7
- data.tar.gz: 7c589af8831eb9596ab2799ae04bd9af6433883f467234e8fe220cd04a6bfc526e31aed74d4b5671330ab5034fc207cbb54c34fc5854f1ae291b431e6985de40
6
+ metadata.gz: 564d87a5328385d750e0734363a7dae2a20e56469801b4042818deb6c6f81468cdf0172d80bc47b09a432fb03b0834147771f11c6b939e10544b3d73952b450c
7
+ data.tar.gz: 4aa7af6322c6489d26733ff50117e3341d50fe68ecdba7846642149cbccb2874619483aa4a5bf0debacbb4d2ed93d2b5ca9eaf5507ca57afa755190c6dda2724
@@ -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.19'
4
+ s.date = '2025-11-03'
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
@@ -478,8 +547,25 @@ class AdsPowerClient
478
547
  end
479
548
 
480
549
  def driver2(id, headless: false, read_timeout: 180)
481
- return @@drivers[id] if @@drivers[id]
482
-
550
+ #return @@drivers[id] if @@drivers[id]
551
+ # If we have a cached driver, verify it's still valid
552
+ if @@drivers[id]
553
+ begin
554
+ # quick, non-destructive sanity check: ask for window_handles
555
+ @@drivers[id].window_handles
556
+ return @@drivers[id]
557
+ rescue Selenium::WebDriver::Error::InvalidSessionIdError,
558
+ Selenium::WebDriver::Error::NoSuchWindowError,
559
+ Errno::ECONNREFUSED => e
560
+ # stale/broken driver: best-effort cleanup and continue to create a new one
561
+ #warn "detected stale driver for #{id}: #{e.class}: #{e.message}"
562
+ self.class.cleanup(id)
563
+ rescue => e
564
+ #warn "unexpected error checking cached driver: #{e.class}: #{e.message}"
565
+ self.class.cleanup(id)
566
+ end
567
+ end
568
+
483
569
  # 1) start the AdsPower profile / grab its WebSocket URL
484
570
  data = start(id, headless)['data']
485
571
  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.19
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-11-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: uri