From 922c3265330b70a1ab560614418891595297d18d Mon Sep 17 00:00:00 2001 From: zarazaex69 Date: Tue, 12 May 2026 21:05:12 +0300 Subject: [PATCH 1/2] fix(provider/wbstream): update API endpoints and WebSocket URL - Switched getToken endpoint to /api-room-manager/v2/room/%s/connection-details - Updated WebSocket URL to wss://rtc-el-01.wb.ru - Updated Python PoC scripts to match new endpoints --- code/wbstream_info.py | 15 +++++++-------- code/wbstream_poc_datachannel.py | 11 +++++------ code/wbstream_poc_videochannel.py | 4 ++-- internal/provider/wbstream/api.go | 3 ++- internal/provider/wbstream/api_test.go | 4 ++-- internal/provider/wbstream/peer.go | 2 +- 6 files changed, 19 insertions(+), 20 deletions(-) diff --git a/code/wbstream_info.py b/code/wbstream_info.py index bb32507..11bb629 100755 --- a/code/wbstream_info.py +++ b/code/wbstream_info.py @@ -5,20 +5,20 @@ import requests from livekit import rtc API_BASE = "https://stream.wb.ru" -WS_URL = "wss://wbstream01-el.wb.ru:7880" +WS_URL = "wss://rtc-el-01.wb.ru" def _get_room_token(room_id: str, display_name: str) -> tuple[str, str]: headers = {"User-Agent": "Mozilla/5.0 (Linux x86_64)", "Content-Type": "application/json"} - + print("[1/3] API Initialization...") reg_req = requests.post(f"{API_BASE}/auth/api/v1/auth/user/guest-register", json={"displayName": display_name, "device": {"deviceName": "Linux", "deviceType": "PARTICIPANT_DEVICE_TYPE_WEB_DESKTOP"}}, headers=headers) reg_req.raise_for_status() auth_data = reg_req.json() print(" :P Guest registered") print(json.dumps(auth_data, indent=2)) - + headers["Authorization"] = f"Bearer {auth_data['accessToken']}" - + if not room_id: print("\n[2/3] Room Preparation...") room_req = requests.post(f"{API_BASE}/api-room/api/v2/room", json={"roomType": "ROOM_TYPE_ALL_ON_SCREEN", "roomPrivacy": "ROOM_PRIVACY_FREE"}, headers=headers) @@ -27,17 +27,16 @@ def _get_room_token(room_id: str, display_name: str) -> tuple[str, str]: print(" :P Room created") print(json.dumps(room_data, indent=2)) room_id = room_data["roomId"] - + print(f"\n[3/3] Fetching LiveKit token...") requests.post(f"{API_BASE}/api-room/api/v1/room/{room_id}/join", json={}, headers=headers).raise_for_status() - tok_req = requests.get(f"{API_BASE}/api-room-manager/api/v1/room/{room_id}/token", params={"deviceType": "PARTICIPANT_DEVICE_TYPE_WEB_DESKTOP", "displayName": display_name}, headers=headers) + tok_req = requests.get(f"{API_BASE}/api-room-manager/v2/room/{room_id}/connection-details", params={"deviceType": "PARTICIPANT_DEVICE_TYPE_WEB_DESKTOP", "displayName": display_name}, headers=headers) tok_req.raise_for_status() token_data = tok_req.json() print(" :P Token received") print(json.dumps(token_data, indent=2)) - - return room_id, token_data["roomToken"] + return room_id, token_data["roomToken"] async def get_wb_info(): print("\n--- WB Stream Info ---") try: diff --git a/code/wbstream_poc_datachannel.py b/code/wbstream_poc_datachannel.py index b4685dc..2850294 100755 --- a/code/wbstream_poc_datachannel.py +++ b/code/wbstream_poc_datachannel.py @@ -15,7 +15,7 @@ except ImportError: logging.getLogger("livekit").setLevel(logging.WARNING) API_BASE = "https://stream.wb.ru" -WS_URL = "wss://wbstream01-el.wb.ru:7880" +WS_URL = "wss://rtc-el-01.wb.ru" TEST_MESSAGES = ["Hello WB Stream!", "Hello world", "X" * 100, "Final test"] def _get_room_token(room_id: str, display_name: str) -> tuple[str, str]: @@ -24,7 +24,7 @@ def _get_room_token(room_id: str, display_name: str) -> tuple[str, str]: "User-Agent": "Mozilla/5.0 (Linux x86_64)", "Content-Type": "application/json" } - + reg_req = requests.post( f"{API_BASE}/auth/api/v1/auth/user/guest-register", json={"displayName": display_name, "device": {"deviceName": "Linux", "deviceType": "PARTICIPANT_DEVICE_TYPE_WEB_DESKTOP"}}, @@ -32,17 +32,16 @@ def _get_room_token(room_id: str, display_name: str) -> tuple[str, str]: ) reg_req.raise_for_status() headers["Authorization"] = f"Bearer {reg_req.json()['accessToken']}" - + if not room_id: room_req = requests.post(f"{API_BASE}/api-room/api/v2/room", json={"roomType": "ROOM_TYPE_ALL_ON_SCREEN", "roomPrivacy": "ROOM_PRIVACY_FREE"}, headers=headers) room_req.raise_for_status() room_id = room_req.json()["roomId"] - + requests.post(f"{API_BASE}/api-room/api/v1/room/{room_id}/join", json={}, headers=headers).raise_for_status() - tok_req = requests.get(f"{API_BASE}/api-room-manager/api/v1/room/{room_id}/token", params={"deviceType": "PARTICIPANT_DEVICE_TYPE_WEB_DESKTOP", "displayName": display_name}, headers=headers) + tok_req = requests.get(f"{API_BASE}/api-room-manager/v2/room/{room_id}/connection-details", params={"deviceType": "PARTICIPANT_DEVICE_TYPE_WEB_DESKTOP", "displayName": display_name}, headers=headers) tok_req.raise_for_status() return room_id, tok_req.json()["roomToken"] - async def run_poc() -> dict: """Runs the complete PoC flow.""" print("\n--- WB Stream PoC ---") diff --git a/code/wbstream_poc_videochannel.py b/code/wbstream_poc_videochannel.py index 1eae686..51b7e6d 100755 --- a/code/wbstream_poc_videochannel.py +++ b/code/wbstream_poc_videochannel.py @@ -41,7 +41,7 @@ def _decode(arr: np.ndarray) -> str | None: except Exception: pass return None - +WS_URL = "wss://rtc-el-01.wb.ru" def _get_room_token(room_id: str, display_name: str) -> tuple[str, str]: headers = {"User-Agent": "Mozilla/5.0 (Linux x86_64)", "Content-Type": "application/json"} @@ -56,7 +56,7 @@ def _get_room_token(room_id: str, display_name: str) -> tuple[str, str]: r.raise_for_status() room_id = r.json()["roomId"] requests.post(f"{API_BASE}/api-room/api/v1/room/{room_id}/join", json={}, headers=headers).raise_for_status() - tok = requests.get(f"{API_BASE}/api-room-manager/api/v1/room/{room_id}/token", + tok = requests.get(f"{API_BASE}/api-room-manager/v2/room/{room_id}/connection-details", params={"deviceType": "PARTICIPANT_DEVICE_TYPE_WEB_DESKTOP", "displayName": display_name}, headers=headers) tok.raise_for_status() return room_id, tok.json()["roomToken"] diff --git a/internal/provider/wbstream/api.go b/internal/provider/wbstream/api.go index 7b991a1..8e2f580 100644 --- a/internal/provider/wbstream/api.go +++ b/internal/provider/wbstream/api.go @@ -46,6 +46,7 @@ type createRoomResponse struct { type tokenResponse struct { RoomToken string `json:"roomToken"` + ServerURL string `json:"serverUrl"` } func registerGuest(ctx context.Context, displayName string) (string, error) { @@ -164,7 +165,7 @@ func joinRoom(ctx context.Context, accessToken, roomID string) error { } func getToken(ctx context.Context, accessToken, roomID, displayName string) (string, error) { - u := fmt.Sprintf("%s/api-room-manager/api/v1/room/%s/token", apiBase, roomID) + u := fmt.Sprintf("%s/api-room-manager/v2/room/%s/connection-details", apiBase, roomID) req, err := http.NewRequestWithContext(ctx, http.MethodGet, u, nil) if err != nil { return "", fmt.Errorf("create request: %w", err) diff --git a/internal/provider/wbstream/api_test.go b/internal/provider/wbstream/api_test.go index 1ec6b26..d52f13e 100644 --- a/internal/provider/wbstream/api_test.go +++ b/internal/provider/wbstream/api_test.go @@ -37,7 +37,7 @@ func TestWBStreamAPIHappyPath(t *testing.T) { _ = json.NewEncoder(w).Encode(createRoomResponse{RoomID: "room"}) //nolint:goconst,lll // test literal, repetition is intentional case "/api-room/api/v1/room/room/join": w.WriteHeader(http.StatusOK) - case "/api-room-manager/api/v1/room/room/token": + case "/api-room-manager/v2/room/room/connection-details": if r.URL.Query().Get("displayName") != "peer" { t.Fatalf("displayName query = %q", r.URL.Query().Get("displayName")) } @@ -103,7 +103,7 @@ func TestWBStreamGetRoomToken(t *testing.T) { _ = json.NewEncoder(w).Encode(createRoomResponse{RoomID: "created"}) case "/api-room/api/v1/room/created/join": w.WriteHeader(http.StatusOK) - case "/api-room-manager/api/v1/room/created/token": + case "/api-room-manager/v2/room/created/connection-details": _ = json.NewEncoder(w).Encode(tokenResponse{RoomToken: "token"}) default: http.NotFound(w, r) diff --git a/internal/provider/wbstream/peer.go b/internal/provider/wbstream/peer.go index 03f5f91..cce330b 100644 --- a/internal/provider/wbstream/peer.go +++ b/internal/provider/wbstream/peer.go @@ -15,7 +15,7 @@ import ( ) const ( - wsURL = "wss://wbstream01-el.wb.ru:7880" + wsURL = "wss://rtc-el-01.wb.ru" ) var ( From d4d71c681032b258ee3fd9d3937356c5ea3ea443 Mon Sep 17 00:00:00 2001 From: zarazaex69 Date: Tue, 12 May 2026 21:31:25 +0300 Subject: [PATCH 2/2] refactor(test): adopt Go 1.22 ServeMux in provider mock servers --- internal/provider/jazz/api_test.go | 83 +++++++++++--------------- internal/provider/telemost/api_test.go | 21 ++++--- internal/provider/wbstream/api_test.go | 74 +++++++++++------------ 3 files changed, 84 insertions(+), 94 deletions(-) diff --git a/internal/provider/jazz/api_test.go b/internal/provider/jazz/api_test.go index 4bdf683..f25a9b0 100644 --- a/internal/provider/jazz/api_test.go +++ b/internal/provider/jazz/api_test.go @@ -6,7 +6,6 @@ import ( "errors" "net/http" "net/http/httptest" - "strings" "testing" ) @@ -21,27 +20,19 @@ func withJazzAPIServer(t *testing.T, h http.Handler) { apiBase = srv.URL } -//nolint:cyclop // table-driven test naturally has many branches func TestCreateMeetingAndPreconnect(t *testing.T) { - withJazzAPIServer(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + mux := http.NewServeMux() + mux.HandleFunc("POST /room/create-meeting", func(w http.ResponseWriter, r *http.Request) { if r.Header.Get(headerAuthType) != authTypeAnonymous { t.Fatalf("missing auth header: %v", r.Header) } - switch r.URL.Path { - case "/room/create-meeting": //nolint:goconst // test literal, repetition is intentional - if r.Method != http.MethodPost { - t.Fatalf("create method = %s", r.Method) - } - _ = json.NewEncoder(w).Encode(createResponse{RoomID: "room-1", Password: "pass"}) //nolint:gosec,lll // G117: test-only struct mirroring upstream API shape - case "/room/room-1/preconnect": - if r.Method != http.MethodPost { - t.Fatalf("preconnect method = %s", r.Method) - } - _ = json.NewEncoder(w).Encode(map[string]string{"connectorUrl": "wss://connector"}) //nolint:goconst,lll // test literal, repetition is intentional - default: - http.NotFound(w, r) - } - })) + _ = json.NewEncoder(w).Encode(createResponse{RoomID: "room-1", Password: "pass"}) //nolint:gosec,lll // G117: test-only struct mirroring upstream API shape + }) + mux.HandleFunc("POST /room/room-1/preconnect", func(w http.ResponseWriter, _ *http.Request) { + _ = json.NewEncoder(w).Encode(map[string]string{"connectorUrl": "wss://connector"}) //nolint:goconst,lll // test literal, repetition is intentional + }) + + withJazzAPIServer(t, mux) headers := map[string]string{ headerAuthType: authTypeAnonymous, @@ -64,18 +55,16 @@ func TestCreateMeetingAndPreconnect(t *testing.T) { } } -//nolint:cyclop // table-driven test naturally has many branches func TestCreateRoomAndJoinRoom(t *testing.T) { - withJazzAPIServer(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - switch r.URL.Path { - case "/room/create-meeting": - _ = json.NewEncoder(w).Encode(createResponse{RoomID: "new-room", Password: "new-pass"}) //nolint:goconst,gosec,lll // test literal; G117 is a false positive for test fixtures - case "/room/new-room/preconnect", "/room/existing/preconnect": - _ = json.NewEncoder(w).Encode(map[string]string{"connectorUrl": "wss://connector"}) - default: - http.NotFound(w, r) - } - })) + mux := http.NewServeMux() + mux.HandleFunc("POST /room/create-meeting", func(w http.ResponseWriter, _ *http.Request) { + _ = json.NewEncoder(w).Encode(createResponse{RoomID: "new-room", Password: "new-pass"}) //nolint:goconst,gosec,lll // test literal; G117 is a false positive for test fixtures + }) + mux.HandleFunc("POST /room/{id}/preconnect", func(w http.ResponseWriter, _ *http.Request) { + _ = json.NewEncoder(w).Encode(map[string]string{"connectorUrl": "wss://connector"}) + }) + + withJazzAPIServer(t, mux) room, err := createRoom(context.Background()) if err != nil { @@ -95,14 +84,15 @@ func TestCreateRoomAndJoinRoom(t *testing.T) { } func TestJazzAPIErrors(t *testing.T) { - withJazzAPIServer(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - switch { - case strings.Contains(r.URL.Path, "create-meeting"): - http.Error(w, "bad", http.StatusTeapot) - default: - http.Error(w, "bad", http.StatusInternalServerError) - } - })) + mux := http.NewServeMux() + mux.HandleFunc("/room/create-meeting", func(w http.ResponseWriter, _ *http.Request) { + http.Error(w, "bad", http.StatusTeapot) + }) + mux.HandleFunc("/", func(w http.ResponseWriter, _ *http.Request) { + http.Error(w, "bad", http.StatusInternalServerError) + }) + + withJazzAPIServer(t, mux) if _, err := createMeeting(context.Background(), nil); !errors.Is(err, errCreateRoomFailed) { t.Fatalf("createMeeting() error = %v, want %v", err, errCreateRoomFailed) @@ -113,16 +103,15 @@ func TestJazzAPIErrors(t *testing.T) { } func TestNewPeerUsesRoomAPI(t *testing.T) { - withJazzAPIServer(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - switch r.URL.Path { - case "/room/create-meeting": - _ = json.NewEncoder(w).Encode(createResponse{RoomID: "new-room", Password: "new-pass"}) //nolint:gosec,lll // G117: test-only struct mirroring upstream API shape - case "/room/new-room/preconnect", "/room/existing/preconnect": - _ = json.NewEncoder(w).Encode(map[string]string{"connectorUrl": "wss://connector"}) - default: - http.NotFound(w, r) - } - })) + mux := http.NewServeMux() + mux.HandleFunc("POST /room/create-meeting", func(w http.ResponseWriter, _ *http.Request) { + _ = json.NewEncoder(w).Encode(createResponse{RoomID: "new-room", Password: "new-pass"}) //nolint:gosec,lll // G117: test-only struct mirroring upstream API shape + }) + mux.HandleFunc("POST /room/{id}/preconnect", func(w http.ResponseWriter, _ *http.Request) { + _ = json.NewEncoder(w).Encode(map[string]string{"connectorUrl": "wss://connector"}) + }) + + withJazzAPIServer(t, mux) created, err := NewPeer(context.Background(), "any", "peer", nil) if err != nil { diff --git a/internal/provider/telemost/api_test.go b/internal/provider/telemost/api_test.go index 1650e60..a42e88f 100644 --- a/internal/provider/telemost/api_test.go +++ b/internal/provider/telemost/api_test.go @@ -22,12 +22,10 @@ func withTelemostAPIServer(t *testing.T, h http.Handler) { } func TestGetConnectionInfo(t *testing.T) { - withTelemostAPIServer(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if r.Method != http.MethodGet { - t.Fatalf("method = %s", r.Method) - } - if !strings.Contains(r.URL.EscapedPath(), "/conferences/room%2Fid/connection") { - t.Fatalf("path = %q escaped=%q", r.URL.Path, r.URL.EscapedPath()) + mux := http.NewServeMux() + mux.HandleFunc("GET /conferences/{id...}", func(w http.ResponseWriter, r *http.Request) { + if !strings.HasPrefix(r.URL.Path, "/conferences/room/id/connection") { + t.Fatalf("path = %q", r.URL.Path) } if r.URL.Query().Get("display_name") != "peer" { t.Fatalf("display_name query = %q", r.URL.Query().Get("display_name")) @@ -37,7 +35,9 @@ func TestGetConnectionInfo(t *testing.T) { PeerID: "peer-id", //nolint:goconst // test literal, repetition is intentional Credentials: "creds", //nolint:goconst // test literal, repetition is intentional }) - })) + }) + + withTelemostAPIServer(t, mux) info, err := GetConnectionInfo(context.Background(), "room/id", "peer") if err != nil { @@ -65,13 +65,16 @@ func TestGetConnectionInfoErrors(t *testing.T) { } func TestTelemostNewPeerUsesConnectionInfo(t *testing.T) { - withTelemostAPIServer(t, http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { + mux := http.NewServeMux() + mux.HandleFunc("GET /", func(w http.ResponseWriter, _ *http.Request) { _ = json.NewEncoder(w).Encode(ConnectionInfo{ RoomID: "room", PeerID: "peer-id", Credentials: "creds", }) - })) + }) + + withTelemostAPIServer(t, mux) p, err := NewPeer(context.Background(), "room", "name", nil) if err != nil { diff --git a/internal/provider/wbstream/api_test.go b/internal/provider/wbstream/api_test.go index d52f13e..2563cb5 100644 --- a/internal/provider/wbstream/api_test.go +++ b/internal/provider/wbstream/api_test.go @@ -20,32 +20,29 @@ func withWBAPIServer(t *testing.T, h http.Handler) { apiBase = srv.URL } -//nolint:cyclop // table-driven test naturally has many branches func TestWBStreamAPIHappyPath(t *testing.T) { - withWBAPIServer(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - switch r.URL.Path { - case "/auth/api/v1/auth/user/guest-register": - if r.Method != http.MethodPost { - t.Fatalf("guest method = %s", r.Method) - } - _ = json.NewEncoder(w).Encode(guestRegisterResponse{AccessToken: "access"}) //nolint:goconst,gosec,lll // test literal; G117 is a false positive for test fixtures - case "/api-room/api/v2/room": - if r.Header.Get("Authorization") != "Bearer access" { - t.Fatalf("room auth = %q", r.Header.Get("Authorization")) - } - w.WriteHeader(http.StatusCreated) - _ = json.NewEncoder(w).Encode(createRoomResponse{RoomID: "room"}) //nolint:goconst,lll // test literal, repetition is intentional - case "/api-room/api/v1/room/room/join": - w.WriteHeader(http.StatusOK) - case "/api-room-manager/v2/room/room/connection-details": - if r.URL.Query().Get("displayName") != "peer" { - t.Fatalf("displayName query = %q", r.URL.Query().Get("displayName")) - } - _ = json.NewEncoder(w).Encode(tokenResponse{RoomToken: "token"}) //nolint:goconst,lll // test literal, repetition is intentional - default: - http.NotFound(w, r) + mux := http.NewServeMux() + mux.HandleFunc("POST /auth/api/v1/auth/user/guest-register", func(w http.ResponseWriter, _ *http.Request) { + _ = json.NewEncoder(w).Encode(guestRegisterResponse{AccessToken: "access"}) //nolint:goconst,gosec,lll // test literal; G117 is a false positive for test fixtures + }) + mux.HandleFunc("POST /api-room/api/v2/room", func(w http.ResponseWriter, r *http.Request) { + if r.Header.Get("Authorization") != "Bearer access" { + t.Fatalf("room auth = %q", r.Header.Get("Authorization")) } - })) + w.WriteHeader(http.StatusCreated) + _ = json.NewEncoder(w).Encode(createRoomResponse{RoomID: "room"}) //nolint:goconst,lll // test literal, repetition is intentional + }) + mux.HandleFunc("POST /api-room/api/v1/room/room/join", func(w http.ResponseWriter, _ *http.Request) { + w.WriteHeader(http.StatusOK) + }) + mux.HandleFunc("GET /api-room-manager/v2/room/room/connection-details", func(w http.ResponseWriter, r *http.Request) { + if r.URL.Query().Get("displayName") != "peer" { + t.Fatalf("displayName query = %q", r.URL.Query().Get("displayName")) + } + _ = json.NewEncoder(w).Encode(tokenResponse{RoomToken: "token"}) //nolint:goconst,lll // test literal, repetition is intentional + }) + + withWBAPIServer(t, mux) access, err := registerGuest(context.Background(), "peer") if err != nil { @@ -95,20 +92,21 @@ func TestWBStreamAPIErrors(t *testing.T) { } func TestWBStreamGetRoomToken(t *testing.T) { - withWBAPIServer(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - switch r.URL.Path { - case "/auth/api/v1/auth/user/guest-register": - _ = json.NewEncoder(w).Encode(guestRegisterResponse{AccessToken: "access"}) //nolint:gosec,lll // G117: test-only struct mirroring upstream API shape - case "/api-room/api/v2/room": - _ = json.NewEncoder(w).Encode(createRoomResponse{RoomID: "created"}) - case "/api-room/api/v1/room/created/join": - w.WriteHeader(http.StatusOK) - case "/api-room-manager/v2/room/created/connection-details": - _ = json.NewEncoder(w).Encode(tokenResponse{RoomToken: "token"}) - default: - http.NotFound(w, r) - } - })) + mux := http.NewServeMux() + mux.HandleFunc("POST /auth/api/v1/auth/user/guest-register", func(w http.ResponseWriter, _ *http.Request) { + _ = json.NewEncoder(w).Encode(guestRegisterResponse{AccessToken: "access"}) //nolint:gosec,lll // G117: test-only struct mirroring upstream API shape + }) + mux.HandleFunc("POST /api-room/api/v2/room", func(w http.ResponseWriter, _ *http.Request) { + _ = json.NewEncoder(w).Encode(createRoomResponse{RoomID: "created"}) + }) + mux.HandleFunc("POST /api-room/api/v1/room/{id}/join", func(w http.ResponseWriter, _ *http.Request) { + w.WriteHeader(http.StatusOK) + }) + mux.HandleFunc("GET /api-room-manager/v2/room/{id}/connection-details", func(w http.ResponseWriter, _ *http.Request) { + _ = json.NewEncoder(w).Encode(tokenResponse{RoomToken: "token"}) + }) + + withWBAPIServer(t, mux) p, err := NewPeer(context.Background(), "any", "peer", nil) if err != nil {