Skip to content

Commit

Permalink
Add signProviderOptions for using non-default signing servers. (#204)
Browse files Browse the repository at this point in the history
* Add "`signProviderOptions`" for using non-default signing servers.

* Improve parameters of `signProviderOptions`.
  • Loading branch information
shaiyon authored Jul 10, 2024
1 parent f31c7de commit b39c08d
Show file tree
Hide file tree
Showing 4 changed files with 32 additions and 10 deletions.
11 changes: 10 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ To create a new `WebcastPushConnection` object the following parameters are requ
| Param Name | Required | Description |
| ---------- | -------- | ----------- |
| uniqueId | Yes | The unique username of the broadcaster. You can find this name in the URL.<br>Example: `https://www.tiktok.com/@officialgeilegisela/live` => `officialgeilegisela` |
| options | No | Here you can set the following optional connection properties. If you do not specify a value, the default value will be used.<br><br>`processInitialData` (default: `true`) <br> Define if you want to process the initital data which includes old messages of the last seconds.<br><br>`fetchRoomInfoOnConnect` (default: `true`) <br> Define if you want to fetch all room information on [`connect()`](#methods). If this option is enabled, the connection to offline rooms will be prevented. If enabled, the connect result contains the room info via the `roomInfo` attribute. You can also manually retrieve the room info (even in an unconnected state) using the [`getRoomInfo()`](#methods) function.<br><br>`enableExtendedGiftInfo` (default: `false`) <br> Define if you want to receive extended information about gifts like gift name, cost and images. This information will be provided at the [gift event](#gift). <br><br>`enableWebsocketUpgrade` (default: `true`) <br> Define if you want to use a WebSocket connection instead of request polling if TikTok offers it. <br><br>`requestPollingIntervalMs` (default: `1000`) <br> Request polling interval if WebSocket is not used.<br><br>`sessionId` (default: `null`) <br> Here you can specify the current Session ID of your TikTok account (**sessionid** cookie value) if you want to send automated chat messages via the [`sendMessage()`](#methods) function. See [Example](#send-chat-messages)<br><br>`clientParams` (default: `{}`) <br> Custom client params for Webcast API.<br><br>`requestHeaders` (default: `{}`) <br> Custom request headers passed to [axios](https://github.com/axios/axios).<br><br>`websocketHeaders` (default: `{}`) <br> Custom websocket headers passed to [websocket.client](https://github.com/theturtle32/WebSocket-Node). <br><br>`requestOptions` (default: `{}`) <br> Custom request options passed to [axios](https://github.com/axios/axios). Here you can specify an `httpsAgent` to use a proxy and a `timeout` value. See [Example](#connect-via-proxy). <br><br>`websocketOptions` (default: `{}`) <br> Custom websocket options passed to [websocket.client](https://github.com/theturtle32/WebSocket-Node). Here you can specify an `agent` to use a proxy and a `timeout` value. See [Example](#connect-via-proxy). |
| options | No | Here you can set the following optional connection properties. If you do not specify a value, the default value will be used.<br><br>`processInitialData` (default: `true`) <br> Define if you want to process the initital data which includes old messages of the last seconds.<br><br>`fetchRoomInfoOnConnect` (default: `true`) <br> Define if you want to fetch all room information on [`connect()`](#methods). If this option is enabled, the connection to offline rooms will be prevented. If enabled, the connect result contains the room info via the `roomInfo` attribute. You can also manually retrieve the room info (even in an unconnected state) using the [`getRoomInfo()`](#methods) function.<br><br>`enableExtendedGiftInfo` (default: `false`) <br> Define if you want to receive extended information about gifts like gift name, cost and images. This information will be provided at the [gift event](#gift). <br><br>`enableWebsocketUpgrade` (default: `true`) <br> Define if you want to use a WebSocket connection instead of request polling if TikTok offers it. <br><br>`requestPollingIntervalMs` (default: `1000`) <br> Request polling interval if WebSocket is not used.<br><br>`sessionId` (default: `null`) <br> Here you can specify the current Session ID of your TikTok account (**sessionid** cookie value) if you want to send automated chat messages via the [`sendMessage()`](#methods) function. See [Example](#send-chat-messages)<br><br>`clientParams` (default: `{}`) <br> Custom client params for Webcast API.<br><br>`requestHeaders` (default: `{}`) <br> Custom request headers passed to [axios](https://github.com/axios/axios).<br><br>`websocketHeaders` (default: `{}`) <br> Custom websocket headers passed to [websocket.client](https://github.com/theturtle32/WebSocket-Node). <br><br>`requestOptions` (default: `{}`) <br> Custom request options passed to [axios](https://github.com/axios/axios). Here you can specify an `httpsAgent` to use a proxy and a `timeout` value. See [Example](#connect-via-proxy). <br><br>`websocketOptions` (default: `{}`) <br> Custom websocket options passed to [websocket.client](https://github.com/theturtle32/WebSocket-Node). Here you can specify an `agent` to use a proxy and a `timeout` value. See [Example](#connect-via-proxy). <br><br>`signProviderOptions` (default: `{}`) <br> Custom request options for the TikTok signing server. Here you can specify a `host`, `params`, and `headers`. |

Example Options:
```javascript
Expand All @@ -95,6 +95,15 @@ let tiktokLiveConnection = new WebcastPushConnection(tiktokUsername, {
},
websocketOptions: {
timeout: 10000
},
signProviderOptions: {
host: "https://custom-signing-server.com",
params: {
"paramName": "paramValue"
},
headers: {
"headerName": "headerValue"
}
}
});
```
Expand Down
6 changes: 4 additions & 2 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,14 +76,15 @@ class WebcastPushConnection extends EventEmitter {
* @param {object} [options[].websocketHeaders={}] Custom request headers for websocket.client
* @param {object} [options[].requestOptions={}] Custom request options for axios. Here you can specify an `httpsAgent` to use a proxy and a `timeout` value for example.
* @param {object} [options[].websocketOptions={}] Custom request options for websocket.client. Here you can specify an `agent` to use a proxy and a `timeout` value for example.
*/
* @param {object} [options[].signProviderOptions={}] Custom request options for the TikTok signing server. Here you can specify a `host`, `params`, and `headers`.
*/
constructor(uniqueId, options) {
super();

this.#setOptions(options || {});

this.#uniqueStreamerId = validateAndNormalizeUniqueId(uniqueId);
this.#httpClient = new TikTokHttpClient(this.#options.requestHeaders, this.#options.requestOptions, this.#options.sessionId);
this.#httpClient = new TikTokHttpClient(this.#options.requestHeaders, this.#options.requestOptions, this.#options.signProviderOptions, this.#options.sessionId);

this.#clientParams = {
...Config.DEFAULT_CLIENT_PARAMS,
Expand All @@ -109,6 +110,7 @@ class WebcastPushConnection extends EventEmitter {
websocketHeaders: Config.DEFAULT_REQUEST_HEADERS,
requestOptions: {},
websocketOptions: {},
signProviderOptions: {},
},
providedOptions
);
Expand Down
6 changes: 4 additions & 2 deletions src/lib/tiktokHttpClient.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ const { signWebcastRequest } = require('./tiktokSignatureProvider');
const Config = require('./webcastConfig.js');

class TikTokHttpClient {
constructor(customHeaders, axiosOptions, sessionId) {
constructor(customHeaders, axiosOptions, signProviderOptions, sessionId) {
const { Cookie } = customHeaders || {};

if (Cookie) {
Expand All @@ -28,6 +28,8 @@ class TikTokHttpClient {
Cookie.split('; ').forEach((v) => this.cookieJar.processSetCookieHeader(v));
}

this.signProviderOptions = signProviderOptions;

if (sessionId) {
this.setSessionId(sessionId);
}
Expand All @@ -51,7 +53,7 @@ class TikTokHttpClient {
let fullUrl = `${host}${path}?${new URLSearchParams(params || {})}`;

if (sign) {
fullUrl = await signWebcastRequest(fullUrl, this.axiosInstance.defaults.headers, this.cookieJar);
fullUrl = await signWebcastRequest(fullUrl, this.axiosInstance.defaults.headers, this.cookieJar, this.signProviderOptions);
}

return fullUrl;
Expand Down
19 changes: 14 additions & 5 deletions src/lib/tiktokSignatureProvider.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,11 @@ let config = {

let signEvents = new EventEmitter();

function signWebcastRequest(url, headers, cookieJar) {
return signRequest('webcast/sign_url', url, headers, cookieJar);
function signWebcastRequest(url, headers, cookieJar, signProviderOptions) {
return signRequest('webcast/sign_url', url, headers, cookieJar, signProviderOptions);
}

async function signRequest(providerPath, url, headers, cookieJar) {
async function signRequest(providerPath, url, headers, cookieJar, signProviderOptions) {
if (!config.enabled) {
return url;
}
Expand All @@ -30,18 +30,27 @@ async function signRequest(providerPath, url, headers, cookieJar) {
url,
client: 'ttlive-node',
...config.extraParams,
...signProviderOptions?.params,
};

params.uuc = getUuc();

let hostsToTry = [config.signProviderHost, ...config.signProviderFallbackHosts];
// Prioritize the custom host if provided
if (signProviderOptions?.host) {
// Remove any existing entries of the custom host to avoid duplication
hostsToTry = hostsToTry.filter((host) => host !== signProviderOptions.host);
hostsToTry.unshift(signProviderOptions.host);
}

let signHost;
let signResponse;
let signError;

try {
for (signHost of [config.signProviderHost, ...config.signProviderFallbackHosts]) {
for (signHost of hostsToTry) {
try {
signResponse = await axios.get(signHost + providerPath, { params, responseType: 'json' });
signResponse = await axios.get(signHost + providerPath, { params, headers: signProviderOptions?.headers, responseType: 'json' });

if (signResponse.status === 200 && typeof signResponse.data === 'object') {
break;
Expand Down

0 comments on commit b39c08d

Please sign in to comment.