Table of Contents

X スペース

client.Spaces(Twikit.Spaces.Spaces)から、X スペースの読み取り・検索・作成・ 公開・モデレーション・終了、HLS ストリーム URL の取得、チャット履歴とライブチャットが使えます。 ライブラリの他の機能と同じく、ログイン済みの Client(auth_token と ct0 の Cookie)が必要です。

Web クライアントが使っている次の API を、そのまま呼び出しています。

用途 API
メタデータ / 検索 GraphQL(AudioSpaceById、AudioSpaceSearch、BrowseSpaceTopics ...)
ブロードキャストのライフサイクル proxsee.pscp.tv(Periscope)v2 API(createBroadcast、publishBroadcast、endBroadcast)
コントロールプレーン(ミュート、設定、終了、承認 ...) guest-cf.pscp.tv /api/v1(chatman)
チャット proxsee accessChat → chatapi v1(HTTP 履歴 + WebSocket)
音声(発言・聴取) Janus WebRTC videoroom

読み取り

var space = await client.Spaces.GetSpaceAsync("1DXGydznBYWKM");   // URL 全体でも可
Console.WriteLine($"{space.State} {space.Title} host={space.HostUserId} speakers={space.SpeakerIds.Count}");

// 配信中のスペースを検索('Top' / 'Live' / 'Upcoming')
var live = await client.Spaces.SearchAsync("music", filter: "Live");

// HLS ストリーム URL(ffmpeg で聴取: ffmpeg -i <url> out.m4a)
var stream = await client.Spaces.GetStreamAsync(space.MediaKey!);
Console.WriteLine(stream.HlsUrl);

// チャット履歴(ライブ中でもリプレイでも)
var chat = await client.Spaces.ChatAsync(space);
foreach (var message in await chat.HistoryAsync(limit: 20))
    Console.WriteLine($"{message.Sender?["username"]}: {message.Body}");

ライブ中のスペースでは chat.IsLive が true になり、ListenAsync() でメッセージを受信、SendAsync(text) で送信できます (WebSocket は .NET 標準の ClientWebSocket を使うため追加パッケージは不要です)。

await foreach (var message in chat.ListenAsync())
    Console.WriteLine(message.Body);

/live-chat の HTTP ストリーム(Web クライアントのサイドバーが使うもの)も StreamLiveChatAsync から読めます。 replay: true を渡すと終了したスペースのチャットログを取得できます。

作成・終了

var created = await client.Spaces.CreateSpaceAsync(title: "こんにちは", conversationControls: 2);
var spaceId = created["broadcast"]!["id"]!.GetValue<string>();

var liveSpace = await client.Spaces.GetSpaceAsync(spaceId);   // State == "Running"
await client.Spaces.EndSpaceAsync(spaceId);

CreateSpaceAsync は Web クライアントと同じ proxsee + Janus の HTTP フロー(Janus ルームの作成、publisher の登録、 publishBroadcast、adminInvite)を実行します。scheduledStartTime を渡すと予約スペースになります (GetScheduledSpacesAsync / CancelScheduledSpaceAsync)。

モデレーション

await client.Spaces.MuteSpaceAsync(spaceId);
await client.Spaces.SetSpaceSettingsAsync(spaceId, conversationControls: 0);  // 0=リクエスト/承認、1=フォロー中、2=全員
await client.Spaces.AddAdminAsync(spaceId, userId);
var status = await client.Spaces.GetCallStatusAsync(spaceId);            // guest_sessions など
await client.Spaces.ApproveAsync(sessionUuid, spaceId);

リスナーとして参加してから発言をリクエストし、ホストの承認を待つこともできます。

var joined = await client.Spaces.JoinAsync(space);
var sessionUuid = await client.Spaces.RequestToSpeakAsync(space);
var guest = await client.Spaces.WaitForSpeakerAsync(space.Id!, sessionUuid, timeoutSeconds: 120);

音声(WebRTC)

発言(SpeakAsync)、ホストの音声(HostAsync)、WebRTC での聴取(ListenAsync)には WebRTC の ピア接続が必要です。Python 版は aiortc を使いますが、.NET 標準に WebRTC の実装は無いため、C# 版では IPeerConnection と IPeerConnectionFactory というインターフェイスを用意しています。 SIPSorcery などで実装して渡してください。

public sealed class MyFactory : IPeerConnectionFactory
{
    public IPeerConnection Create(IReadOnlyList<IceServer> iceServers) => new MyPeerConnection(iceServers);
}

var session = await client.Spaces.SpeakAsync(space, audioTrack: myTrack, peerConnectionFactory: new MyFactory());
// ... 発言中 ...
await session.CloseAsync();

Janus とのシグナリング(JanusClient: create / attach / join / configure / ロングポール)、chatman の negotiate / publish / unmute などの手順は Python 版と同じ順序で実装されているので、実装する必要があるのは SDP の offer / answer と音声トラックの送受信だけです。

Note

既定では TURN を使わず直接接続します。X の TURN サーバー(turns:turn.pscp.tv:443)は約 60 秒で TLS 接続を切り、 メディアの経路が止まるためです。直接接続できない環境では iceServers を明示的に渡してください。

簡単に聴くには

WebRTC を使わなくても、StreamUrlAsync(space) が返す HLS の m3u8 を ffmpeg などで再生すれば聴取できます。

ffmpeg -i "<hls url>" out.m4a