/* * VideoKit * Copyright © 2026 Yusuf Olokoba. All Rights Reserved. */ #nullable enable namespace VideoKit.Internal { using System; using System.Collections.Generic; using System.Net.Http; using System.Net.Http.Headers; using System.Runtime.InteropServices; using System.Text; using System.Threading.Tasks; using UnityEngine; using UnityEngine.Networking; using Muna; using Newtonsoft.Json; using Status = VideoKit.Status; /// /// VideoKit API client. /// [DefaultExecutionOrder(-10_000)] public sealed class VideoKitClient : ScriptableObject { #region --Client API-- /// /// VideoKit API URL. /// [SerializeField, HideInInspector] public string url = URL; /// /// VideoKit Muna client. /// public Muna muna => _muna ??= MunaUnity.Create(authToken, url: $"{url}/muna"); /// /// VideoKit client for this project. /// public static VideoKitClient? Instance { get; internal set; } /// /// VideoKit client version. /// public const string Version = @"1.0.13"; /// /// Check the application VideoKit session status. /// public async Task CheckSession() { try { // Check if initialized if (VideoKit.SetSessionToken(sessionToken) == Status.Ok) return Status.Ok; // Create token var token = await CreateSessionToken(); var status = VideoKit.SetSessionToken(token); if (status == Status.Ok) { sessionToken = token; PlayerPrefs.SetString(SessionTokenKey, token); } // Return return status; } catch (Exception ex) { Debug.LogWarning($"VideoKit: Failed to check session with error: {ex.Message}"); return Status.InvalidOperation; } } /// /// Create a VideoKit client. /// /// VideoKit auth token. /// VideoKit API URL. public static VideoKitClient Create(string? token, string? url = null) { var client = CreateInstance(); client.authToken = token; client.url = !string.IsNullOrEmpty(url) ? url : client.url; return client; } #endregion #region --Operations-- [SerializeField, HideInInspector] private string? authToken = string.Empty; private Muna? _muna; private string? sessionToken; public const string URL = @"https://www.videokit.ai/api"; private const string SessionTokenKey = @"ai.videokit.session"; private void Awake() { // Check editor if (Application.isEditor) return; // Set singleton in player Instance = Instance ? Instance : this; // Set session token sessionToken = ( PlayerPrefs.HasKey(SessionTokenKey) ? PlayerPrefs.GetString(SessionTokenKey) : null ); } internal async static Task CreateAuthToken( string platform, string apiKey, string? url = URL ) { // Create client using var request = new HttpClient(); request.DefaultRequestHeaders.Authorization = !string.IsNullOrEmpty(apiKey) ? new AuthenticationHeaderValue(@"Bearer", apiKey) : null; // Request var version = Marshal.PtrToStringUTF8(VideoKit.GetVersion()); var payload = new Dictionary { [@"platform"] = platform, [@"version"] = version }; var payloadStr = JsonConvert.SerializeObject(payload); using var content = new StringContent(payloadStr, Encoding.UTF8, @"application/json"); using var response = await request.PostAsync($"{url}/build", content); // Parse response var responseStr = await response.Content.ReadAsStringAsync(); Dictionary responseBody; try { responseBody = JsonConvert.DeserializeObject>(responseStr)!; } catch { throw new InvalidOperationException($"Failed to create build token with status {response.StatusCode} and error: {responseStr}"); } // Check error if (responseBody.TryGetValue(@"error", out var error)) throw new InvalidOperationException(error); // Return return responseBody[@"token"]; } private async Task CreateSessionToken() { // Get session id var sessionId = new StringBuilder(2048); VideoKit.GetSessionIdentifier(sessionId, sessionId.Capacity); // Create payload var payload = new Dictionary { [@"buildToken"] = authToken, [@"sessionId"] = sessionId.ToString(), }; var payloadStr = JsonConvert.SerializeObject(payload); // Generate session token using var request = new UnityWebRequest($"{url}/session/v3", UnityWebRequest.kHttpVerbPOST) { uploadHandler = new UploadHandlerRaw(Encoding.UTF8.GetBytes(payloadStr)), downloadHandler = new DownloadHandlerBuffer(), disposeDownloadHandlerOnDispose = true, disposeUploadHandlerOnDispose = true, timeout = 20, }; request.SetRequestHeader(@"Content-Type", @"application/json"); request.SendWebRequest(); while (!request.isDone) await Task.Yield(); // Parse response Dictionary response; try { response = JsonConvert.DeserializeObject>(request.downloadHandler.text)!; } catch { throw new InvalidOperationException($"Failed to create session token with status {request.responseCode} and error: {request.downloadHandler.text}"); } // Check error if (response.TryGetValue(@"error", out var error)) throw new InvalidOperationException(error); // Return return response["token"]; } #endregion } }