conversant 1.0.17 → 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: 964bc9a41a8eaf896bf334a98a80c10abd83ca529fe080c335f293d5ebe94ccc
4
- data.tar.gz: 5b073e3f44dd178f7ac95eca6f0cce42fe6b38e837e32058774c192d561b7374
3
+ metadata.gz: b0bd892aa072333932ee783013051c4a32fc77875c71b128a080e717bdfca10a
4
+ data.tar.gz: b79962fb9f3d80b38b966f9eb14e9ae9192e2e97daae9f0ba998e7cfb5a422c0
5
5
  SHA512:
6
- metadata.gz: c46571c45a041450c1f6e002086eead91811b580f9914ad47d81fc9e39fcb8283152090625b5d0c63f1cc96ffa8b85f51ab3b72385ec99052dcd645e9bc30f6f
7
- data.tar.gz: 25d9221a514ba50bdfe29c6abf456b146837adf8d9eb4bc7e1641cc8d90f27db0e276c499b6ecb7fcb6f2220c6c4f785619cc73d7b430b272760e42ead137510
6
+ metadata.gz: 5f4ad35b17ed3670a73f71c0b67dc9f20ed09384e15c0c42895ff611f4817a3a1f2cbb2e4af4b534bf953a2e83e3d7a6b067afa937c5e33ec00c8f5709b1b71a
7
+ data.tar.gz: f47decce579d7da1b863fd8b580b5972970fd266fe623a204ef8c38f96974f6ba87928cfe7d72a7ea5fbcac671d9b72e1a838bf6707b59006c425109aebcab15
data/CHANGELOG.md CHANGED
@@ -5,6 +5,35 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [1.0.19] - 2026-02-06
9
+
10
+ ### Fixed
11
+ - **LMS Job RBS Type Signatures**: Updated RBS signatures in `sig/conversant/v3/services/lms.rbs` to match actual implementation
12
+ - Fixed `where` return type from `Array[Hash[String, untyped]]` to `Array[Hash[Symbol, untyped]]?` (nilable, symbol keys)
13
+ - Fixed `parse_status` parameter type from `Integer | String | nil` to `Integer` (value is always `.to_i`)
14
+ - Fixed `build_manifest_url` signature to `(String? play_domain, Hash item, String? output_type)` matching actual parameters
15
+ - Removed non-existent `build_profile_url` method signature
16
+
17
+ ## [1.0.18] - 2025-10-08
18
+
19
+ ### Fixed
20
+ - **Portal Service Authentication**: Fixed SESSION cookie handling to use root sessions directly
21
+ - Portal service now uses root SESSION + SSO_GW_SESSION2 cookies directly instead of attempting to exchange them
22
+ - Removed incorrect cookie extraction from response (Portal doesn't return a new SESSION cookie)
23
+ - Changed from extracting `response.cookies['SESSION']` to using `sessions[:session]` from SSO login
24
+ - Resolves "NO_SESSION_IN_RESPONSE" error when calling `portal.appliances`
25
+ - Matches original implementation behavior where root sessions are used for all Portal API calls
26
+
27
+ ### Changed
28
+ - Updated `Portal#fetch_new_session` method to return root SESSION cookie directly after verification
29
+ - Test call to `#{portal_endpoint}/?cId=#{customer_id}` now only validates session instead of extracting cookies
30
+ - Log message changed from "REQUESTING_SESSION" to "USING_ROOT_SESSIONS" for clarity
31
+
32
+ ### Impact
33
+ - Portal service now works correctly without "Missing SESSION for Portal" errors
34
+ - `portal.appliances` and other Portal API methods now authenticate properly
35
+ - Consistent with other services (CDN, LMS, VMS) that also exchange sessions but Portal uses root sessions directly
36
+
8
37
  ## [1.0.17] - 2025-10-01
9
38
 
10
39
  ### Fixed
data/README.md CHANGED
@@ -472,9 +472,9 @@ jobs = lms.job.where(
472
472
  )
473
473
 
474
474
  jobs.each do |job|
475
- puts "Job #{job['id']}: #{job['name']}"
476
- puts " Status: #{job['status']}"
477
- puts " Created: #{job['created_at']}"
475
+ puts "Job #{job[:id]}: #{job[:name]}"
476
+ puts " Status: #{job[:status]}"
477
+ puts " Created: #{job[:created_at]}"
478
478
  end
479
479
 
480
480
  # Domain Management
@@ -834,20 +834,13 @@ end
834
834
 
835
835
  ## Version Notes
836
836
 
837
- ### Latest Release: 1.0.10 (2025-09-30)
837
+ ### Latest Release: 1.0.19 (2026-02-06)
838
838
 
839
- **Documentation Update**: Enhanced documentation for v1.0.9 critical fixes
839
+ **RBS Type Signature Fix**: Updated LMS Job RBS signatures to match actual implementation
840
840
 
841
- ### Version 1.0.9 - Critical Bug Fix
842
-
843
- **Fixed authentication issues with LMS and VMS services:**
844
-
845
- - ✅ Fixed JSESSIONID authentication for LMS service (resolves "Missing JSESSIONID" error)
846
- - ✅ Fixed JSESSIONID authentication for VMS service
847
- - ✅ Improved RestClient cookie handling (switched from manual `'Cookie'` header to `:cookies` option)
848
- - ✅ Fixed method visibility issues in Authorization module
849
-
850
- **Upgrade recommended** if you're using LMS or VMS services. CDN and Portal services are unaffected.
841
+ - Fixed `where` return type to `Array[Hash[Symbol, untyped]]?`
842
+ - Fixed `build_manifest_url` parameter signature
843
+ - Removed non-existent `build_profile_url` method signature
851
844
 
852
845
  See [CHANGELOG.md](CHANGELOG.md) for complete version history.
853
846
 
@@ -4,9 +4,9 @@ module Conversant
4
4
  module V3
5
5
  module Services
6
6
  class LMS
7
- # Job management service for live streaming operations
7
+ # Job management service for live-streaming operations
8
8
  #
9
- # Provides comprehensive job management functionality for live streaming including:
9
+ # Provides comprehensive job management functionality for live-streaming including:
10
10
  # - Job listing and filtering
11
11
  # - Job status tracking
12
12
  # - Stream profile information
@@ -61,9 +61,15 @@ module Conversant
61
61
  # - :encoder [String] encoder IP address
62
62
  # - :server [String] execution server
63
63
  # - :manifest [String] manifest URL
64
- # - :profiles [Array<Hash>] array of stream profiles
64
+ # - :profiles_count [Integer] number of stream profiles
65
65
  # - :transcoding [Boolean] transcoding enabled
66
66
  # - :gpu [Boolean] GPU acceleration enabled
67
+ # - :ha [Boolean] high availability enabled
68
+ # - :dvr [Boolean] DVR enabled
69
+ # - :dai [Boolean] dynamic ad insertion enabled
70
+ # - :encrypted [Boolean] stream encryption enabled
71
+ # - :ingest_url [String] RTMP ingest URL
72
+ # - :play_domain [String] playback domain
67
73
  #
68
74
  # @example Get streaming jobs
69
75
  # jobs = lms.job.where(status: 'streaming', limit: 50)
@@ -79,19 +85,14 @@ module Conversant
79
85
  response = JSON.parse(@parent.send(:call, 'GET', "/v4/streams?#{params.to_query}"))
80
86
  return nil unless response
81
87
 
82
- items = response['list'] || response[:list] || []
88
+ items = [response['list'] || response[:list] || []].flatten.map(&:with_indifferent_access)
83
89
 
84
90
  items.map do |item|
85
- status = item['status']&.to_i || item[:status]&.to_i
86
- inbound = item['stream_rtmp_inbound'] || item[:stream_rtmp_inbound]
87
-
88
- profiles = (item['stream_rtmp_outbounds'] || item[:stream_rtmp_outbounds])&.map do |stream|
89
- {
90
- preset: stream['preset_name'] || stream[:preset_name],
91
- type: stream['type'] || stream[:type],
92
- profile: build_profile_url(inbound, item, stream)
93
- }
94
- end
91
+ status = item[:status]&.to_i
92
+ inbound = item[:stream_rtmp_inbound]
93
+ play_domain = item[:play_domain] || item['play_domain']
94
+ outbounds = item[:stream_rtmp_outbounds] || item['stream_rtmp_outbounds'] || []
95
+ output_type = outbounds.first && (outbounds.first['type'] || outbounds.first[:type])
95
96
 
96
97
  {
97
98
  entity: item['osp_id'] || item[:osp_id],
@@ -103,12 +104,18 @@ module Conversant
103
104
  started_at: parse_timestamp(item['started'] || item[:started]),
104
105
  ended_at: parse_timestamp(item['ended'] || item[:ended]),
105
106
  client: inbound && (inbound['client_ip'] || inbound[:client_ip]),
107
+ ingest_url: inbound&.dig(:push_url) || inbound&.dig('push_url'),
106
108
  encoder: extract_encoder(item),
107
109
  server: item['exec_ta'] || item[:exec_ta],
108
- manifest: build_manifest_url(inbound, item, profiles),
109
- profiles: profiles || [],
110
+ manifest: build_manifest_url(play_domain, item, output_type),
111
+ profiles_count: outbounds.size,
110
112
  transcoding: item['transcoding'] || item[:transcoding],
111
- gpu: (item['gpu'] || item[:gpu]) == 'gpu'
113
+ gpu: (item['gpu'] || item[:gpu]) == 'gpu',
114
+ ha: item['ha'] || item[:ha],
115
+ dvr: item['dvr'] || item[:dvr],
116
+ dai: item['dai'] || item[:dai],
117
+ encrypted: item[:stream_encrypts]&.any? || item['stream_encrypts']&.any?,
118
+ play_domain: play_domain
112
119
  }
113
120
  end
114
121
  rescue StandardError => e
@@ -121,13 +128,19 @@ module Conversant
121
128
  # Parse numeric status code to string
122
129
  #
123
130
  # @param status [Integer] numeric status code
124
- # @return [String, Integer] status string or original code if unknown
131
+ # @return [String] status string or 'unknown_N' if unknown
125
132
  def parse_status(status)
126
133
  case status
127
134
  when 1 then 'ready'
128
135
  when 2 then 'streaming'
136
+ when 3 then 'stopped'
137
+ when 4 then 'error'
138
+ when 5 then 'starting'
139
+ when 6 then 'stopping'
140
+ when 7 then 'restarting'
141
+ when 8 then 'pending'
129
142
  when 12 then 'interrupted'
130
- else status
143
+ else "unknown_#{status}"
131
144
  end
132
145
  end
133
146
 
@@ -164,48 +177,23 @@ module Conversant
164
177
  end
165
178
  end
166
179
 
167
- # Build profile URL for a stream
168
- #
169
- # @param inbound [Hash] inbound stream configuration
170
- # @param item [Hash] job item hash
171
- # @param stream [Hash] stream profile configuration
172
- # @return [String, nil] profile URL or nil if inbound is missing
173
- def build_profile_url(inbound, item, stream)
174
- return nil unless inbound
175
-
176
- domain = inbound['domain'] || inbound[:domain]
177
- app = item['app'] || item[:app]
178
- stream_name = item['stream'] || item[:stream]
179
- preset = stream['preset_name'] || stream[:preset_name]
180
- type = stream['type'] || stream[:type]
181
-
182
- "http://#{domain}/#{app}/#{stream_name}-#{preset}#{stream_extension(type)}"
183
- end
184
-
185
180
  # Build manifest URL for a job
186
181
  #
187
- # @param inbound [Hash] inbound stream configuration
182
+ # @param play_domain [String] playback domain
188
183
  # @param item [Hash] job item hash
189
- # @param profiles [Array<Hash>] array of stream profiles
190
- # @return [String, nil] manifest URL or nil if inbound is missing
191
- def build_manifest_url(inbound, item, profiles)
192
- return nil unless inbound
184
+ # @param output_type [String] output stream type ('hls', 'dash')
185
+ # @return [String, nil] manifest URL or nil if play_domain is missing
186
+ def build_manifest_url(play_domain, item, output_type)
187
+ return nil unless play_domain
193
188
 
194
- domain = inbound['domain'] || inbound[:domain]
195
189
  app = item['app'] || item[:app]
196
190
  manifest_name = item['manifest_name'] || item[:manifest_name]
191
+ ext = stream_extension(output_type)
197
192
 
198
- ext = if profiles&.first
199
- type = profiles.first[:type]
200
- stream_extension(type)
201
- else
202
- ''
203
- end
204
-
205
- "http://#{domain}/#{app}/#{manifest_name}#{ext}"
193
+ "https://#{play_domain}/#{app}/#{manifest_name}#{ext}"
206
194
  end
207
195
  end
208
196
  end
209
197
  end
210
198
  end
211
- end
199
+ end
@@ -146,17 +146,18 @@ module Conversant
146
146
  # Fetches a new SESSION cookie for Portal API access
147
147
  #
148
148
  # Authenticates with root portal sessions (SESSION + SSO_GW_SESSION2) and
149
- # exchanges them for a customer-specific SESSION cookie. The new session
150
- # is cached in Redis with the configured TTL.
149
+ # uses them directly for Portal API calls. Makes a test call to verify
150
+ # the sessions are valid.
151
151
  #
152
152
  # @return [String, nil] The SESSION cookie value, or nil if authentication fails
153
153
  def fetch_new_session
154
154
  sessions = authenticate
155
155
  return nil unless sessions && sessions[:session] && sessions[:sso_gw_session2]
156
156
 
157
- logger.debug "#{identifier}.METHOD:authorize.REQUESTING_SESSION"
157
+ logger.debug "#{identifier}.METHOD:authorize.USING_ROOT_SESSIONS"
158
158
 
159
- response = RestClient.get(
159
+ # Make a test call to verify sessions are valid
160
+ RestClient.get(
160
161
  "#{portal_endpoint}/?cId=#{customer_id}",
161
162
  {
162
163
  authority: URI.parse(portal_endpoint).hostname,
@@ -171,15 +172,11 @@ module Conversant
171
172
  }
172
173
  )
173
174
 
174
- if response.cookies['SESSION']
175
- session_cookie = response.cookies['SESSION']
176
- redis.set(session_cache_key, session_cookie, ex: configuration.cache_ttl)
177
- logger.debug "#{identifier}.METHOD:authorize.SESSION_OBTAINED"
178
- session_cookie
179
- else
180
- logger.error "#{identifier}.METHOD:authorize.NO_SESSION_IN_RESPONSE"
181
- nil
182
- end
175
+ # Cache and return the root SESSION cookie
176
+ session_cookie = sessions[:session]
177
+ redis.set(session_cache_key, session_cookie, ex: configuration.cache_ttl)
178
+ logger.debug "#{identifier}.METHOD:authorize.SESSION_OBTAINED"
179
+ session_cookie
183
180
  rescue RestClient::Unauthorized, RestClient::Forbidden => e
184
181
  logger.error "#{identifier}.METHOD:authorize.AUTH_ERROR:#{e.message}"
185
182
  nil
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Conversant
4
- VERSION = '1.0.17'
4
+ VERSION = '1.0.19'
5
5
  end
@@ -9,16 +9,15 @@ module Conversant
9
9
  attr_reader parent: LMS
10
10
 
11
11
  def initialize: (LMS parent) -> void
12
- def where: (**untyped params) -> Array[Hash[String, untyped]]
12
+ def where: (**untyped params) -> Array[Hash[Symbol, untyped]]?
13
13
 
14
14
  private
15
15
 
16
- def parse_status: (Integer | String | nil status) -> String
16
+ def parse_status: (Integer status) -> String
17
17
  def parse_timestamp: (Integer | String | nil timestamp) -> DateTime?
18
18
  def extract_encoder: (Hash[String | Symbol, untyped] item) -> String
19
19
  def stream_extension: (String? type) -> String
20
- def build_profile_url: (Hash[String | Symbol, untyped]? inbound, Hash[String | Symbol, untyped] item, Hash[String | Symbol, untyped]? stream) -> String?
21
- def build_manifest_url: (Hash[String | Symbol, untyped]? inbound, Hash[String | Symbol, untyped] item, Array[Hash[Symbol, untyped]]? profiles) -> String?
20
+ def build_manifest_url: (String? play_domain, Hash[String | Symbol, untyped] item, String? output_type) -> String?
22
21
  end
23
22
 
24
23
  # Domain management for LMS
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: conversant
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.17
4
+ version: 1.0.19
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tho Nguyen
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2025-10-01 00:00:00.000000000 Z
11
+ date: 2026-02-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: json