Skip to content

Commit

Permalink
Merge pull request #83 from frankvHoof93/development
Browse files Browse the repository at this point in the history
V1.1.1 - Bugfix of Websocket-Client
  • Loading branch information
frankvHoof93 authored May 28, 2024
2 parents ac4390b + dfb7a11 commit 251cebf
Show file tree
Hide file tree
Showing 21 changed files with 224 additions and 92 deletions.
19 changes: 19 additions & 0 deletions CHANGELOG.MD
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,25 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [1.1.1] - Websocket Bugfix

Bugfix for failing Websocket-Connection

### Added

- RunAsync for TTLiveSharp, for more control over Threads
- Settings for Custom Signing-Server & Signing-ApiKey
- Additional StatusCode-Messaging for Signing-Server

### Fixed

- Fixed incorrect Header(s) on WebSocket-Client
- Fixed AlreadyConnectedException when retrying a Connect()

### Changed

- Replaced HTTPUtility with WebUtility for URL-Encoding

## [1.1.0] -

Bugfix for failing/closing Websocket-Connections
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# TikTokLiveSharp / TikTokLiveUnity v1.1.0
# TikTokLiveSharp / TikTokLiveUnity v1.1.1

[![LinkedIn](https://img.shields.io/badge/LinkedIn-0077B5?style=for-the-badge&logo=linkedin&logoColor=white&style=flat-square)](https://www.linkedin.com/in/frankvhoof93/ )
![Issues](https://img.shields.io/github/issues/frankvHoof93/TikTokLiveSharp)
Expand Down
2 changes: 1 addition & 1 deletion Setup_CSharp.MD
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# TikTokLiveSharp v1.1.0
# TikTokLiveSharp v1.1.1

## Downloading the Source Code
Download the TikTokLiveSharp-SourceCode from the [Releases-Page](https://github.com/frankvHoof93/TikTokLiveSharp/releases/)
Expand Down
2 changes: 1 addition & 1 deletion Setup_Unity.MD
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# TikTokLiveUnity v1.1.0
# TikTokLiveUnity v1.1.1

## Installing the Project in Unity
- Open the Package Manager Window **(Window -> Package Manager)**
Expand Down
19 changes: 19 additions & 0 deletions TikTokLiveSharp/Client/Config/ClientSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,25 @@ public struct ClientSettings
public bool DownloadGiftInfo;
#endregion

#region Signing
/// <summary>
/// API-Key for Signing Server
/// </summary>
#if UNITY
[UnityEngine.Header("Signing-Server")]
[UnityEngine.Tooltip("API-Key for Signing Server")]
#endif
public string SigningKey;

/// <summary>
/// Custom URL for Signing Server
/// </summary>
#if UNITY
[UnityEngine.Tooltip("Custom URL for Signing Server")]
#endif
public string CustomSigningServerUrl;
#endregion

#region Debug
/// <summary>
/// Whether to print Logs to Console
Expand Down
93 changes: 55 additions & 38 deletions TikTokLiveSharp/Client/Config/Constants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,19 @@ public static class Constants
/// <summary>
/// Web-URL for TikTok
/// </summary>
public const string TIKTOK_URL_WEB = "https://www.tiktok.com/";
public const string TIKTOK_URL_WEB = @"https://www.tiktok.com/";
/// <summary>
/// WebCast-BaseURL for TikTok
/// </summary>
public const string TIKTOK_URL_WEBCAST = "https://webcast.tiktok.com/webcast/";
public const string TIKTOK_URL_WEBCAST = @"https://webcast.tiktok.com/webcast/";
/// <summary>
/// Signing API by Isaac Kogan
/// </summary>
public const string TIKTOK_SIGN_API = "https://tiktok.eulerstream.com/webcast/fetch";

public const string TIKTOK_SIGN_API = @"https://tiktok.eulerstream.com/webcast/fetch";
/// <summary>
/// User-Agent for WebClients (Http/WebSocket)
/// </summary>
public const string USER_AGENT = "5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.5005.63 Safari/537.36";
/// <summary>
/// Default TimeOut for Connections
/// </summary>
Expand Down Expand Up @@ -62,51 +65,65 @@ public static class Constants
public static readonly IReadOnlyDictionary<string, object> DEFAULT_CLIENT_PARAMS = new ReadOnlyDictionary<string, object>(new Dictionary<string, object>()
{
{ "aid", 1988 },
{ "app_language", "en-US" },
{ "app_name", "tiktok_web" },
{ "browser_language", "en-US" },
{ "browser_name", "Mozilla" },
{ "browser_online", true },
{ "browser_platform", "Win32" },
{ "browser_version", "5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.0.0 Safari/537.36" },
{ "cookie_enabled", true },
{ "cursor", "" },
{ "internal_ext", "" },
{ "device_platform", "web" },
{ "focus_state", true },
{ "from_page", "user" },
{ "history_len", 4 },
{ "is_fullscreen", false },
{ "is_page_visible", true },
{ "did_rule", 3 },
{ "fetch_rule", 1 },
{ "last_rtt", 0 },
{ "live_id", 12 },
{ "resp_content_type", "protobuf" },
{ "screen_height", 1152 },
{ "screen_width", 2048 },
{ "tz_name", "Europe/London" },
{ "referer", "https, //www.tiktok.com/" },
{ "root_referer", "https, //www.tiktok.com/" },
{ "app_language", "en-US" },
{ "app_name", "tiktok_web" },
{ "browser_language", "en" },
{ "browser_name", "Mozilla" },
{ "browser_online", true },
{ "browser_platform", "Win32" },
{ "browser_version", USER_AGENT },
{ "cookie_enabled", true },
{ "cursor", "" },
{ "internal_ext", "" },
{ "device_platform", "web" },
{ "focus_state", true },
{ "from_page", "user" },
{ "history_len", 4 },
{ "is_fullscreen", false },
{ "is_page_visible", true },
{ "did_rule", 3 },
{ "fetch_rule", 1 },
{ "last_rtt", 0 },
{ "live_id", 12 },
{ "resp_content_type", "protobuf" },
{ "screen_height", 1080 },
{ "screen_width", 1920 },
{ "tz_name", "Europe/Berlin" },
{ "referer", "https, //www.tiktok.com/" },
{ "root_referer", "https, //www.tiktok.com/" },
{ "msToken", "" },
{ "version_code", 180800 },
{ "webcast_sdk_version", "1.3.0" },
{ "update_version_code", "1.3.0" }
{ "version_code", 180800 },
{ "webcast_sdk_version", "1.3.0" },
{ "update_version_code", "1.3.0" }
});

/// <summary>
/// Default Headers for HTTP-Request
/// </summary>
public static readonly IReadOnlyDictionary<string, string> DEFAULT_REQUEST_HEADERS = new ReadOnlyDictionary<string, string>(new Dictionary<string, string>()
public static readonly IReadOnlyDictionary<string, string> DEFAULT_HTTP_HEADERS = new ReadOnlyDictionary<string, string>(new Dictionary<string, string>()
{
{ "Connection", "keep-alive" },
{ "authority", "www.tiktok.com" },
{ "Cache-Control", "no-cache" },
{ "Accept", "text/html,application/json,application/protobuf" },
{ "User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.5005.63 Safari/537.36" },
{ "Referer", "https://www.tiktok.com/" },
{ "Origin", "https://www.tiktok.com" },
{ "Accept-Language", "en-US,en; q=0.8" }
{ "User-Agent", USER_AGENT },
{ "Referer", TIKTOK_URL_WEB },
{ "Origin", TIKTOK_URL_WEB },
{ "Accept-Language", "en; q=0.8" }
});

/// <summary>
/// Default Headers for Websocket-Client
/// </summary>
public static readonly IReadOnlyDictionary<string, string> DEFAULT_SOCKET_HEADERS = new ReadOnlyDictionary<string, string>(new Dictionary<string, string>()
{
{ "authority", "www.tiktok.com" },
{ "Cache-Control", "max-age=0" },
{ "Accept", "text/html,application/json,application/protobuf" },
{ "User-Agent", USER_AGENT },
{ "Referer", TIKTOK_URL_WEB },
{ "Origin", TIKTOK_URL_WEB },
{ "Accept-Language", "en; q=0.8" }
});

public static readonly KeyValuePair<string, string> COMPRESSION_HEADER = new KeyValuePair<string, string>( "Accept-Encoding", "gzip, deflate" );
Expand Down
5 changes: 5 additions & 0 deletions TikTokLiveSharp/Client/HTTP/TikTokCookieJar.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@ public string this[string key]
/// </summary>
public int Count => cookies?.Count ?? 0;

/// <summary>
/// Cookies in Jar
/// </summary>
public IDictionary<string, string> Cookies => cookies;

/// <summary>
/// Cookies in Jar
/// </summary>
Expand Down
37 changes: 25 additions & 12 deletions TikTokLiveSharp/Client/HTTP/TikTokHTTPClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -118,15 +118,20 @@ internal async Task<string> GetProfilePage(string uniqueId, IDictionary<string,
return await get.ReadAsStringAsync();
}

internal async Task<TikTokWebSocketConnectionData> GetSignedWebsocketData(string roomId)
internal async Task<TikTokWebSocketConnectionData> GetSignedWebsocketData(string roomId, string customServerUrl = null, string apiKey = null)
{
ITikTokHttpRequest request = new TikTokHttpRequest(Constants.TIKTOK_SIGN_API, false, false)
.SetQueries(new Dictionary<string, object>()
{
{ "client", CLIENT_NAME },
{ "uuc", clientNum },
{ "room_id", roomId }
});
Dictionary<string, object> queries = new Dictionary<string, object>()
{
{ "client", CLIENT_NAME },
{ "uuc", clientNum },
{ "room_id", roomId },
};
if (!string.IsNullOrEmpty(apiKey))
{
queries.Add("apiKey", apiKey);
}
ITikTokHttpRequest request = new TikTokHttpRequest(string.IsNullOrEmpty(customServerUrl) ? Constants.TIKTOK_SIGN_API : customServerUrl, false, false)
.SetQueries(queries);
HttpResponseMessage response = await request.GetResponse();
if (response.StatusCode == HttpStatusCode.NotFound)
throw new HttpRequestException("Request responded with 404 NOT_FOUND");
Expand All @@ -137,24 +142,32 @@ internal async Task<TikTokWebSocketConnectionData> GetSignedWebsocketData(string
if (response.Headers.TryGetValues("RateLimit-Reset", out IEnumerable<string> rateHeaders))
{
TimeSpan span = TimeSpan.FromSeconds(long.Parse(rateHeaders.First()));
throw new HttpRequestException($"[{(int)response.StatusCode}] Rate Limit Reached. Try again in {span:mm\\:ss}.");
throw new HttpRequestException($"[{(int)response.StatusCode}] Signing Rate Limit Reached. Try again in {span:mm\\:ss}.");
}
else
{
throw new HttpRequestException($"[{(int)response.StatusCode}] Rate Limit Reached.");
throw new HttpRequestException($"[{(int)response.StatusCode}] Signing Rate Limit Reached.");
}
}
else if ((int)response.StatusCode == 502) // Bad Gateway
{
throw new HttpRequestException($"[{(int)response.StatusCode}] Signing Server not reachable.");
}
else if ((int)response.StatusCode == 503) // Unavailable
{
throw new HttpRequestException($"[{(int)response.StatusCode}] Signing Server unavailable.");
}
else
{
throw new HttpRequestException($"Request was unsuccessful [{(int)response.StatusCode}].");
throw new HttpRequestException($"Signing request was unsuccessful [{(int)response.StatusCode}].");
}
}
if (response.Headers.TryGetValues("x-set-tt-cookie", out IEnumerable<string> cookieHeaders))
{
try
{
Response signingResponse = Serializer.Deserialize<Response>(await response.Content.ReadAsStreamAsync());
return new TikTokWebSocketConnectionData(roomId, cookieHeaders.First(), signingResponse);
return new TikTokWebSocketConnectionData(roomId, cookieHeaders, signingResponse);
}
catch (Exception ex)
{
Expand Down
5 changes: 2 additions & 3 deletions TikTokLiveSharp/Client/HTTP/TikTokHttpRequest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
using System.Web;
using TikTokLiveSharp.Client.Config;

namespace TikTokLiveSharp.Client.HTTP
Expand Down Expand Up @@ -157,7 +156,7 @@ public TikTokHttpRequest(string url, bool enableCompression = true, bool useCook
{
Timeout = Timeout
};
foreach (KeyValuePair<string, string> header in Constants.DEFAULT_REQUEST_HEADERS)
foreach (KeyValuePair<string, string> header in Constants.DEFAULT_HTTP_HEADERS)
client.DefaultRequestHeaders.Add(header.Key, header.Value);
if (enableCompression)
client.DefaultRequestHeaders.Add(Constants.COMPRESSION_HEADER.Key, Constants.COMPRESSION_HEADER.Value);
Expand Down Expand Up @@ -210,7 +209,7 @@ public ITikTokHttpRequest SetQueries(IDictionary<string, object> queries)
{
if (queries == null)
return this;
query = string.Join("&", queries.Select(x => $"{x.Key}={HttpUtility.UrlEncode(x.Value.ToString())}"));
query = string.Join("&", queries.Select(x => $"{x.Key}={WebUtility.UrlEncode(x.Value.ToString())}"));
return this;
}

Expand Down
16 changes: 12 additions & 4 deletions TikTokLiveSharp/Client/Socket/TikTokWebSocket.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using TikTokLiveSharp.Client.Config;
using TikTokLiveSharp.Client.HTTP;

namespace TikTokLiveSharp.Client.Socket
Expand Down Expand Up @@ -73,11 +74,18 @@ public TikTokWebSocket(TikTokCookieJar cookieContainer, uint buffSize = 500_000,
clientWebSocket.Options.Proxy = webProxy;
clientWebSocket.Options.AddSubProtocol("echo-protocol");
clientWebSocket.Options.KeepAliveInterval = TimeSpan.Zero;
if (headers.ContainsKey("ttwid"))
cookieContainer["ttwid"] = headers["ttwid"];
Dictionary<string, string> combinedCookies = new Dictionary<string, string>(cookieContainer.Cookies);
foreach (KeyValuePair<string, string> overrideCookie in headers)
{
combinedCookies[overrideCookie.Key] = overrideCookie.Value;
}
StringBuilder cookieHeader = new StringBuilder(cookieContainer.Count * 20);
foreach (string cookie in cookieContainer)
cookieHeader.Append(cookie);
foreach (string additionalHeader in headers.Values)
cookieHeader.Append(additionalHeader);
foreach (KeyValuePair<string, string> cookie in combinedCookies)
cookieHeader.Append($"{cookie.Key}={cookie.Value};");
foreach (KeyValuePair<string, string> header in Constants.DEFAULT_SOCKET_HEADERS)
clientWebSocket.Options.SetRequestHeader(header.Key, header.Value);
clientWebSocket.Options.SetRequestHeader("Cookie", cookieHeader.ToString());
}
#endregion
Expand Down
24 changes: 17 additions & 7 deletions TikTokLiveSharp/Client/Socket/TikTokWebSocketConnectionData.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
using System.Collections.Generic;
using System.Linq;
using TikTokLiveSharp.Models.Protobuf.Messages;

namespace TikTokLiveSharp.Client.Socket
Expand All @@ -12,25 +14,33 @@ public readonly struct TikTokWebSocketConnectionData
/// </summary>
public readonly string RoomId;
/// <summary>
/// Required Cookies obtained from signing-server Headers
/// </summary>
public readonly string WebSocketCookies;
/// <summary>
/// TikTokServer-Response obtained from signing-server
/// </summary>
public readonly Response InitialWebcastResponse;
/// <summary>
/// Headers required for Websocket-Connection
/// </summary>
public readonly Dictionary<string, string> CookieHeaders;

/// <summary>
/// Creates instance of TikTokWebSocketConnectionData
/// </summary>
/// <param name="roomId">RoomID for stream to connect to</param>
/// <param name="cookies">Required Cookies obtained from signing-server Headers</param>
/// <param name="initialResponse">TikTokServer-Response obtained from signing-server</param>
public TikTokWebSocketConnectionData(string roomId, string cookies, Response initialResponse)
public TikTokWebSocketConnectionData(string roomId, IEnumerable<string> cookies, Response initialResponse)
{
RoomId = roomId;
WebSocketCookies = cookies;
InitialWebcastResponse = initialResponse;
CookieHeaders = new Dictionary<string, string>(cookies.Count());
foreach (string val in cookies)
{
string[] cookieString = val.Split(';', System.StringSplitOptions.RemoveEmptyEntries);
foreach (string cookie in cookieString)
{
string[] parsed = cookie.Split('=');
CookieHeaders.Add(parsed[0], parsed[1]);
}
}
}
}
}
Loading

0 comments on commit 251cebf

Please sign in to comment.