@@ -132,3 +132,210 @@ async def create_stream(self, bucket: str, stream: str) -> Dict[str, Any]:
132132 "message" : f"Failed to create stream: { text } " ,
133133 "status_code" : status
134134 }
135+
136+ async def bind_push_domain (self , bucket : str , domain : str , domain_type : str = "pushRtmp" ) -> Dict [str , Any ]:
137+ """
138+ Bind a push domain to the bucket
139+
140+ Args:
141+ bucket: The bucket name
142+ domain: The push domain to bind (e.g., "mcp-push1.qiniu.com")
143+ domain_type: The domain type (default: "pushRtmp")
144+
145+ Returns:
146+ Dict containing the response status and message
147+ """
148+ base_url = self ._build_bucket_url (bucket )
149+ url = f"{ base_url } ?pushDomain"
150+ headers = self ._get_auth_header ()
151+ headers ["Content-Type" ] = "application/json"
152+
153+ payload = {
154+ "domain" : domain ,
155+ "type" : domain_type
156+ }
157+
158+ logger .info (f"Binding push domain: { domain } (type: { domain_type } ) to bucket: { bucket } " )
159+
160+ async with aiohttp .ClientSession () as session :
161+ async with session .post (url , headers = headers , json = payload ) as response :
162+ status = response .status
163+ text = await response .text ()
164+
165+ if status == 200 or status == 201 :
166+ logger .info (f"Successfully bound push domain: { domain } to bucket: { bucket } " )
167+ return {
168+ "status" : "success" ,
169+ "bucket" : bucket ,
170+ "domain" : domain ,
171+ "type" : domain_type ,
172+ "message" : f"Push domain '{ domain } ' bound successfully to bucket '{ bucket } '" ,
173+ "status_code" : status
174+ }
175+ else :
176+ logger .error (f"Failed to bind push domain: { domain } , status: { status } , response: { text } " )
177+ return {
178+ "status" : "error" ,
179+ "bucket" : bucket ,
180+ "domain" : domain ,
181+ "message" : f"Failed to bind push domain: { text } " ,
182+ "status_code" : status
183+ }
184+
185+ async def bind_play_domain (self , bucket : str , domain : str , domain_type : str = "live" ) -> Dict [str , Any ]:
186+ """
187+ Bind a play domain to the bucket
188+
189+ Args:
190+ bucket: The bucket name
191+ domain: The play domain to bind (e.g., "mcp-play1.qiniu.com")
192+ domain_type: The domain type (default: "live")
193+
194+ Returns:
195+ Dict containing the response status and message
196+ """
197+ base_url = self ._build_bucket_url (bucket )
198+ url = f"{ base_url } ?domain"
199+ headers = self ._get_auth_header ()
200+ headers ["Content-Type" ] = "application/json"
201+
202+ payload = {
203+ "domain" : domain ,
204+ "type" : domain_type
205+ }
206+
207+ logger .info (f"Binding play domain: { domain } (type: { domain_type } ) to bucket: { bucket } " )
208+
209+ async with aiohttp .ClientSession () as session :
210+ async with session .post (url , headers = headers , json = payload ) as response :
211+ status = response .status
212+ text = await response .text ()
213+
214+ if status == 200 or status == 201 :
215+ logger .info (f"Successfully bound play domain: { domain } to bucket: { bucket } " )
216+ return {
217+ "status" : "success" ,
218+ "bucket" : bucket ,
219+ "domain" : domain ,
220+ "type" : domain_type ,
221+ "message" : f"Play domain '{ domain } ' bound successfully to bucket '{ bucket } '" ,
222+ "status_code" : status
223+ }
224+ else :
225+ logger .error (f"Failed to bind play domain: { domain } , status: { status } , response: { text } " )
226+ return {
227+ "status" : "error" ,
228+ "bucket" : bucket ,
229+ "domain" : domain ,
230+ "message" : f"Failed to bind play domain: { text } " ,
231+ "status_code" : status
232+ }
233+
234+ def get_push_urls (self , push_domain : str , bucket : str , stream_name : str ) -> Dict [str , Any ]:
235+ """
236+ Get push URLs for a stream
237+
238+ Args:
239+ push_domain: The push domain (e.g., "mcp-push1.qiniu.com")
240+ bucket: The bucket name
241+ stream_name: The stream name
242+
243+ Returns:
244+ Dict containing RTMP and WHIP push URLs
245+ """
246+ rtmp_url = f"rtmp://{ push_domain } /{ bucket } /{ stream_name } "
247+ whip_url = f"https://{ push_domain } /{ bucket } /{ stream_name } .whip"
248+
249+ return {
250+ "status" : "success" ,
251+ "push_domain" : push_domain ,
252+ "bucket" : bucket ,
253+ "stream" : stream_name ,
254+ "rtmp_url" : rtmp_url ,
255+ "whip_url" : whip_url ,
256+ "message" : f"Push URLs generated for stream '{ stream_name } '"
257+ }
258+
259+ def get_play_urls (self , play_domain : str , bucket : str , stream_name : str ) -> Dict [str , Any ]:
260+ """
261+ Get play URLs for a stream
262+
263+ Args:
264+ play_domain: The play domain (e.g., "mcp-play1.qiniu.com")
265+ bucket: The bucket name
266+ stream_name: The stream name
267+
268+ Returns:
269+ Dict containing FLV, HLS (M3U8), and WHEP play URLs
270+ """
271+ flv_url = f"https://{ play_domain } /{ bucket } /{ stream_name } .flv"
272+ m3u8_url = f"https://{ play_domain } /{ bucket } /{ stream_name } .m3u8"
273+ whep_url = f"https://{ play_domain } /{ bucket } /{ stream_name } .whep"
274+
275+ return {
276+ "status" : "success" ,
277+ "play_domain" : play_domain ,
278+ "bucket" : bucket ,
279+ "stream" : stream_name ,
280+ "flv_url" : flv_url ,
281+ "m3u8_url" : m3u8_url ,
282+ "whep_url" : whep_url ,
283+ "message" : f"Play URLs generated for stream '{ stream_name } '"
284+ }
285+
286+ async def query_traffic_stats (self , begin : str , end : str , g : str = "5min" ,
287+ select : str = "flow" , flow : str = "downflow" ) -> Dict [str , Any ]:
288+ """
289+ Query live streaming traffic statistics
290+
291+ Args:
292+ begin: Start time in format YYYYMMDDHHMMSS (e.g., "20240101000000")
293+ end: End time in format YYYYMMDDHHMMSS (e.g., "20240129105148")
294+ g: Time granularity (default: "5min")
295+ select: Select parameter (default: "flow")
296+ flow: Flow type (default: "downflow")
297+
298+ Returns:
299+ Dict containing traffic statistics
300+ """
301+ if not self .endpoint_url :
302+ raise ValueError ("QINIU_ENDPOINT_URL is not configured" )
303+
304+ # Remove protocol if present in endpoint_url
305+ endpoint = self .endpoint_url
306+ if endpoint .startswith ("http://" ):
307+ endpoint = endpoint [7 :]
308+ elif endpoint .startswith ("https://" ):
309+ endpoint = endpoint [8 :]
310+
311+ # Build URL with query parameters
312+ url = f"http://{ endpoint } ?trafficStats&begin={ begin } &end={ end } &g={ g } &select={ select } &flow={ flow } "
313+ headers = self ._get_auth_header ()
314+ headers ["Content-Type" ] = "application/json"
315+
316+ logger .info (f"Querying traffic stats from { begin } to { end } " )
317+
318+ async with aiohttp .ClientSession () as session :
319+ async with session .get (url , headers = headers ) as response :
320+ status = response .status
321+ text = await response .text ()
322+
323+ if status == 200 :
324+ logger .info (f"Successfully retrieved traffic stats" )
325+ return {
326+ "status" : "success" ,
327+ "begin" : begin ,
328+ "end" : end ,
329+ "data" : text ,
330+ "message" : f"Traffic statistics retrieved successfully" ,
331+ "status_code" : status
332+ }
333+ else :
334+ logger .error (f"Failed to query traffic stats, status: { status } , response: { text } " )
335+ return {
336+ "status" : "error" ,
337+ "begin" : begin ,
338+ "end" : end ,
339+ "message" : f"Failed to query traffic stats: { text } " ,
340+ "status_code" : status
341+ }
0 commit comments