tiktok-live-api 1.2.4 → 1.2.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/bin/demo.mjs +157 -58
  2. package/package.json +1 -1
package/bin/demo.mjs CHANGED
@@ -10,36 +10,72 @@ import path from 'path';
10
10
 
11
11
  const REACT_TEMPLATE = [
12
12
  '"use client";',
13
- "import React, { useEffect, useState } from 'react';",
13
+ "import React, { useEffect, useState, useRef } from 'react';",
14
14
  "import { TikTokLive } from 'tiktok-live-api';",
15
15
  "",
16
16
  "export default function TikTokLiveComponent() {",
17
17
  " const [events, setEvents] = useState([]);",
18
18
  " const [username, setUsername] = useState('aljazeeraenglish');",
19
+ " const [apiKey, setApiKey] = useState('demo_tiktokliveapi_public_2026');",
20
+ " const [inputUsername, setInputUsername] = useState('aljazeeraenglish');",
19
21
  " const [connected, setConnected] = useState(false);",
20
- " ",
22
+ " const clientRef = useRef(null);",
23
+ "",
21
24
  " useEffect(() => {",
22
- " const client = new TikTokLive(username, { apiKey: 'demo_tiktokliveapi_public_2026' });",
25
+ " if (!username) return;",
26
+ " if (clientRef.current) { clientRef.current.disconnect(); }",
27
+ " setEvents([]);",
28
+ " setConnected(false);",
23
29
  " ",
24
- ' client.on(\'chat\', (e) => setEvents(prev => [...prev.slice(-20), "💬 " + e.user.uniqueId + ": " + e.comment]));',
25
- ' client.on(\'gift\', (e) => setEvents(prev => [...prev.slice(-20), "🎁 " + e.user.uniqueId + " sent " + e.giftName]));',
30
+ " const client = new TikTokLive(username, { apiKey });",
31
+ " clientRef.current = client;",
32
+ " ",
33
+ " client.on('chat', (e) => setEvents(prev => [...prev.slice(-40), \"💬 \" + e.user.uniqueId + \": \" + e.comment]));",
34
+ " client.on('gift', (e) => setEvents(prev => [...prev.slice(-40), \"🎁 \" + e.user.uniqueId + \" sent \" + e.giftName]));",
35
+ " client.on('member', (e) => setEvents(prev => [...prev.slice(-40), \"👋 \" + e.user.uniqueId + \" joined\"]));",
36
+ " client.on('like', (e) => setEvents(prev => [...prev.slice(-40), \"❤️ \" + e.user.uniqueId + \" liked\"]));",
26
37
  " client.on('connected', () => setConnected(true));",
27
38
  " client.on('disconnected', () => setConnected(false));",
28
39
  " ",
29
- " client.connect();",
30
- " return () => client.disconnect();",
31
- " }, [username]);",
40
+ " client.connect().catch(() => setConnected(false));",
41
+ " ",
42
+ " return () => { if (clientRef.current) { clientRef.current.disconnect(); } };",
43
+ " }, [username, apiKey]);",
44
+ "",
45
+ " const handleConnect = (e) => {",
46
+ " e.preventDefault();",
47
+ " setUsername(inputUsername);",
48
+ " };",
32
49
  "",
33
50
  " return (",
34
- " <div style={{ fontFamily: 'sans-serif', maxWidth: 400, margin: '40px auto', border: '1px solid #e5e7eb', padding: '24px', borderRadius: '12px', boxShadow: '0 10px 15px -3px rgba(0, 0, 0, 0.1)' }}>",
35
- " <h2 style={{ margin: '0 0 16px 0', fontSize: '1.25rem', fontWeight: 600 }}>TikTok Live SDK Test</h2>",
36
- " <div style={{ marginBottom: '16px', display: 'flex', alignItems: 'center', gap: '8px' }}>",
37
- " <div style={{ width: 10, height: 10, borderRadius: '50%', background: connected ? '#10b981' : '#ef4444' }}></div>",
38
- " <span style={{ fontSize: '0.875rem', color: '#374151' }}>{connected ? 'Connected to @' + username : 'Disconnected'}</span>",
51
+ " <div style={{ fontFamily: 'system-ui, sans-serif', maxWidth: 600, margin: '40px auto', border: '1px solid #e5e7eb', borderRadius: '12px', boxShadow: '0 10px 15px -3px rgba(0, 0, 0, 0.1)', background: '#fff' }}>",
52
+ " <div style={{ padding: '24px', borderBottom: '1px solid #e5e7eb' }}>",
53
+ " <h2 style={{ margin: '0 0 8px 0', fontSize: '1.5rem', fontWeight: 600 }}>TikTok Live SDK</h2>",
54
+ " <p style={{ margin: 0, fontSize: '0.875rem', color: '#6b7280' }}>",
55
+ " Get your own API key and unlock unlimited connections at <a href=\"https://tik.tools\" target=\"_blank\" style={{ color: '#3b82f6', textDecoration: 'none' }}>tik.tools</a>",
56
+ " </p>",
39
57
  " </div>",
40
- " <div style={{ height: 320, overflowY: 'auto', background: '#f9fafb', padding: '12px', borderRadius: '8px', border: '1px solid #e5e7eb', fontSize: '0.875rem' }}>",
41
- " {events.map((e, i) => <div key={i} style={{ padding: '6px 0', borderBottom: '1px solid #f3f4f6' }}>{e}</div>)}",
42
- " {events.length === 0 && <div style={{ color: '#9ca3af', textAlign: 'center', marginTop: 120 }}>Waiting for events...</div>}",
58
+ " <div style={{ padding: '24px' }}>",
59
+ " <form onSubmit={handleConnect} style={{ display: 'flex', flexDirection: 'column', gap: '16px', marginBottom: '24px' }}>",
60
+ " <div>",
61
+ " <label style={{ display: 'block', fontSize: '0.875rem', fontWeight: 500, marginBottom: '6px' }}>TikTok Username</label>",
62
+ " <input type=\"text\" value={inputUsername} onChange={(e) => setInputUsername(e.target.value)} style={{ width: '100%', padding: '8px 12px', borderRadius: '6px', border: '1px solid #d1d5db', boxSizing: 'border-box' }} placeholder=\"e.g. aljazeeraenglish\" />",
63
+ " </div>",
64
+ " <div>",
65
+ " <label style={{ display: 'block', fontSize: '0.875rem', fontWeight: 500, marginBottom: '6px' }}>API Key</label>",
66
+ " <input type=\"text\" value={apiKey} onChange={(e) => setApiKey(e.target.value)} style={{ width: '100%', padding: '8px 12px', borderRadius: '6px', border: '1px solid #d1d5db', boxSizing: 'border-box', fontFamily: 'monospace' }} />",
67
+ " <p style={{ margin: '4px 0 0 0', fontSize: '0.75rem', color: '#9ca3af' }}>The demo key is rate-limited. Replace with your own key for production.</p>",
68
+ " </div>",
69
+ " <button type=\"submit\" style={{ background: '#000', color: '#fff', padding: '10px 16px', borderRadius: '6px', border: 'none', fontWeight: 500, cursor: 'pointer' }}>Connect Stream</button>",
70
+ " </form>",
71
+ " <div style={{ marginBottom: '16px', display: 'flex', alignItems: 'center', gap: '8px' }}>",
72
+ " <div style={{ width: 10, height: 10, borderRadius: '50%', background: connected ? '#10b981' : '#ef4444', transition: 'background 0.3s' }}></div>",
73
+ " <span style={{ fontSize: '0.875rem', color: '#374151', fontWeight: 500 }}>{connected ? 'Connected to @' + username : 'Disconnected'}</span>",
74
+ " </div>",
75
+ " <div style={{ height: 350, overflowY: 'auto', background: '#f9fafb', padding: '12px', borderRadius: '8px', border: '1px solid #e5e7eb', fontSize: '0.875rem', display: 'flex', flexDirection: 'column' }}>",
76
+ " {events.map((e, i) => <div key={i} style={{ padding: '6px 0', borderBottom: '1px solid #f3f4f6', wordBreak: 'break-word' }}>{e}</div>)}",
77
+ " {events.length === 0 && <div style={{ color: '#9ca3af', textAlign: 'center', margin: 'auto 0' }}>{connected ? 'Waiting for events...' : 'Connect to a valid stream to see events'}</div>}",
78
+ " </div>",
43
79
  " </div>",
44
80
  " </div>",
45
81
  " );",
@@ -48,17 +84,36 @@ const REACT_TEMPLATE = [
48
84
 
49
85
  const VUE_TEMPLATE = [
50
86
  "<template>",
51
- ' <div style="font-family: sans-serif; max-width: 400px; margin: 40px auto; border: 1px solid #e5e7eb; padding: 24px; border-radius: 12px; box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1)">',
52
- ' <h2 style="margin: 0 0 16px 0; font-size: 1.25rem; font-weight: 600">TikTok Live SDK Test</h2>',
53
- ' <div style="margin-bottom: 16px; display: flex; align-items: center; gap: 8px">',
54
- " <div :style=\"{ width: '10px', height: '10px', borderRadius: '50%', background: connected ? '#10b981' : '#ef4444' }\"></div>",
55
- " <span style=\"font-size: 0.875rem; color: '#374151'\">{{ connected ? 'Connected to @' + username : 'Disconnected' }}</span>",
87
+ ' <div style="font-family: system-ui, sans-serif; max-width: 600px; margin: 40px auto; border: 1px solid #e5e7eb; border-radius: 12px; box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1); background: #fff">',
88
+ ' <div style="padding: 24px; border-bottom: 1px solid #e5e7eb">',
89
+ ' <h2 style="margin: 0 0 8px 0; font-size: 1.5rem; font-weight: 600">TikTok Live SDK</h2>',
90
+ ' <p style="margin: 0; font-size: 0.875rem; color: #6b7280">',
91
+ ' Get your own API key and unlock unlimited connections at <a href="https://tik.tools" target="_blank" style="color: #3b82f6; text-decoration: none">tik.tools</a>',
92
+ " </p>",
56
93
  " </div>",
57
- ' <div style="height: 320px; overflow-y: auto; background: #f9fafb; padding: 12px; border-radius: 8px; border: 1px solid #e5e7eb; font-size: 0.875rem">',
58
- ' <div v-for="(e, i) in events" :key="i" style="padding: 6px 0; border-bottom: 1px solid #f3f4f6">',
59
- " {{ e }}",
94
+ ' <div style="padding: 24px">',
95
+ ' <form @submit.prevent="handleConnect" style="display: flex; flex-direction: column; gap: 16px; margin-bottom: 24px">',
96
+ " <div>",
97
+ ' <label style="display: block; font-size: 0.875rem; font-weight: 500; margin-bottom: 6px">TikTok Username</label>',
98
+ ' <input type="text" v-model="inputUsername" style="width: 100%; padding: 8px 12px; border-radius: 6px; border: 1px solid #d1d5db; box-sizing: border-box" placeholder="e.g. aljazeeraenglish" />',
99
+ " </div>",
100
+ " <div>",
101
+ ' <label style="display: block; font-size: 0.875rem; font-weight: 500; margin-bottom: 6px">API Key</label>',
102
+ ' <input type="text" v-model="apiKey" style="width: 100%; padding: 8px 12px; border-radius: 6px; border: 1px solid #d1d5db; box-sizing: border-box; font-family: monospace" />',
103
+ ' <p style="margin: 4px 0 0 0; font-size: 0.75rem; color: #9ca3af">The demo key is rate-limited. Replace with your own key for production.</p>',
104
+ " </div>",
105
+ ' <button type="submit" style="background: #000; color: #fff; padding: 10px 16px; border-radius: 6px; border: none; font-weight: 500; cursor: pointer">Connect Stream</button>',
106
+ " </form>",
107
+ ' <div style="margin-bottom: 16px; display: flex; align-items: center; gap: 8px">',
108
+ " <div :style=\"{ width: '10px', height: '10px', borderRadius: '50%', background: connected ? '#10b981' : '#ef4444', transition: 'background 0.3s' }\"></div>",
109
+ " <span style=\"font-size: 0.875rem; color: '#374151'; font-weight: 500\">{{ connected ? 'Connected to @' + activeUsername : 'Disconnected' }}</span>",
110
+ " </div>",
111
+ ' <div style="height: 350px; overflow-y: auto; background: #f9fafb; padding: 12px; border-radius: 8px; border: 1px solid #e5e7eb; font-size: 0.875rem; display: flex; flex-direction: column">',
112
+ ' <div v-for="(e, i) in events" :key="i" style="padding: 6px 0; border-bottom: 1px solid #f3f4f6; word-break: break-word">',
113
+ " {{ e }}",
114
+ " </div>",
115
+ ' <div v-if="events.length === 0" style="color: #9ca3af; text-align: center; margin: auto 0">{{ connected ? "Waiting for events..." : "Connect to a valid stream to see events" }}</div>',
60
116
  " </div>",
61
- ' <div v-if="events.length === 0" style="color: #9ca3af; text-align: center; margin-top: 120px">Waiting for events...</div>',
62
117
  " </div>",
63
118
  " </div>",
64
119
  "</template>",
@@ -67,28 +122,55 @@ const VUE_TEMPLATE = [
67
122
  "import { ref, onMounted, onUnmounted } from 'vue'",
68
123
  "import { TikTokLive } from 'tiktok-live-api'",
69
124
  "",
70
- "const username = ref('aljazeeraenglish')",
125
+ "const inputUsername = ref('aljazeeraenglish')",
126
+ "const activeUsername = ref('aljazeeraenglish')",
127
+ "const apiKey = ref('demo_tiktokliveapi_public_2026')",
71
128
  "const connected = ref(false)",
72
129
  "const events = ref([])",
73
130
  "let client = null",
74
131
  "",
75
- "onMounted(() => {",
76
- " client = new TikTokLive(username.value, { apiKey: 'demo_tiktokliveapi_public_2026' })",
132
+ "const connectClient = () => {",
133
+ " if (client) client.disconnect()",
134
+ " events.value = []",
135
+ " connected.value = false",
136
+ " activeUsername.value = inputUsername.value",
137
+ " ",
138
+ " if (!activeUsername.value) return",
139
+ " ",
140
+ " client = new TikTokLive(activeUsername.value, { apiKey: apiKey.value })",
77
141
  " ",
78
142
  " client.on('chat', (e) => {",
79
143
  ' events.value.push("💬 " + e.user.uniqueId + ": " + e.comment)',
80
- " if (events.value.length > 20) events.value.shift()",
144
+ " if (events.value.length > 40) events.value.shift()",
81
145
  " })",
82
146
  " ",
83
147
  " client.on('gift', (e) => {",
84
148
  ' events.value.push("🎁 " + e.user.uniqueId + " sent " + e.giftName)',
85
- " if (events.value.length > 20) events.value.shift()",
149
+ " if (events.value.length > 40) events.value.shift()",
150
+ " })",
151
+ "",
152
+ " client.on('member', (e) => {",
153
+ ' events.value.push("👋 " + e.user.uniqueId + " joined")',
154
+ " if (events.value.length > 40) events.value.shift()",
155
+ " })",
156
+ " ",
157
+ " client.on('like', (e) => {",
158
+ ' events.value.push("❤️ " + e.user.uniqueId + " liked")',
159
+ " if (events.value.length > 40) events.value.shift()",
86
160
  " })",
87
161
  "",
88
162
  " client.on('connected', () => connected.value = true)",
89
163
  " client.on('disconnected', () => connected.value = false)",
90
164
  " ",
91
- " client.connect()",
165
+ " client.connect().catch(() => connected.value = false)",
166
+ "}",
167
+ "",
168
+ "const handleConnect = () => {",
169
+ " connectClient()",
170
+ "}",
171
+ "",
172
+ "onMounted(() => {",
173
+ " connectClient()",
92
174
  "})",
93
175
  "",
94
176
  "onUnmounted(() => {",
@@ -119,21 +201,16 @@ if (!isInteractive) {
119
201
 
120
202
  console.log("\n " + B + "tiktok-live-api" + R + "\n Unofficial TikTok LIVE SDK by TikTool\n");
121
203
  console.log(" What would you like to do?");
122
- console.log(" 1) " + C.cyan + "Watch Live Terminal Demo" + R + " (Default)");
204
+ console.log(" 1) " + C.cyan + "Watch Live Terminal Demo" + R);
123
205
  console.log(" 2) " + C.green + "Scaffold a Nuxt 3 (Vue) App" + R);
124
206
  console.log(" 3) " + C.blue + "Scaffold a Next.js (React) App" + R);
125
207
  console.log(" 4) " + C.yellow + "Scaffold a Vite (React) App" + R + "\n");
126
208
 
127
- const timeoutId = setTimeout(() => {
128
- console.log(D + "\r No input received. Auto-starting Terminal Demo..." + R + "\n");
129
- rl.close();
130
- runDemo();
131
- }, 10000);
132
-
133
- rl.question(" Choose [1-4]: ", (answer) => {
134
- clearTimeout(timeoutId);
135
- handleMenu(answer.trim() || '1');
136
- });
209
+ function promptAction() {
210
+ rl.question(" Choose [1-4]: ", (answer) => {
211
+ handleMenu(answer.trim());
212
+ });
213
+ }
137
214
 
138
215
  function handleMenu(choice) {
139
216
  if (choice === '1') {
@@ -143,39 +220,58 @@ if (!isInteractive) {
143
220
 
144
221
  if (!['2', '3', '4'].includes(choice)) {
145
222
  console.log(" " + C.red + "Invalid choice." + R);
146
- rl.close();
147
- process.exit(1);
223
+ return promptAction();
148
224
  }
149
225
 
150
226
  rl.question("\n Target directory (e.g. ./my-app) [./tiktok-live-project]: ", (dir) => {
151
227
  dir = dir.trim() || './tiktok-live-project';
152
- rl.close();
153
- runScaffold(choice, dir);
228
+
229
+ console.log("\n Select Package Manager:");
230
+ console.log(" 1) npm " + D + "(Default)" + R);
231
+ console.log(" 2) yarn");
232
+ console.log(" 3) pnpm");
233
+ console.log(" 4) bun");
234
+
235
+ rl.question("\n Choose [1-4]: ", (pmAnswer) => {
236
+ const pmChoice = pmAnswer.trim() || '1';
237
+ const pmMap = { '1': 'npm', '2': 'yarn', '3': 'pnpm', '4': 'bun' };
238
+ const pm = pmMap[pmChoice] || 'npm';
239
+
240
+ rl.close();
241
+ runScaffold(choice, dir, pm);
242
+ });
154
243
  });
155
244
  }
245
+
246
+ promptAction();
156
247
  }
157
248
 
158
249
  // ── Scaffolding Logic ───────────────────────────────────────────────────
159
250
 
160
- function runScaffold(choice, targetDir) {
251
+ function runScaffold(choice, targetDir, pm) {
161
252
  const isNuxt = choice === '2';
162
253
  const isNext = choice === '3';
163
254
  const isVite = choice === '4';
164
255
  const fullPath = path.resolve(process.cwd(), targetDir);
165
256
 
166
- console.log("\n " + B + "🚀 Scaffolding project into " + targetDir + "..." + R + "\n");
257
+ const npxCmd = pm === 'bun' ? 'bunx' : (pm === 'pnpm' ? 'pnpm dlx' : 'npx');
258
+ const installCmd = pm === 'npm' ? 'npm install tiktok-live-api' : pm === 'yarn' ? 'yarn add tiktok-live-api' : pm === 'pnpm' ? 'pnpm add tiktok-live-api' : 'bun add tiktok-live-api';
259
+ const devCmd = pm === 'npm' ? 'npm run dev' : pm === 'yarn' ? 'yarn dev' : pm === 'pnpm' ? 'pnpm dev' : 'bun run dev';
260
+
261
+ console.log("\n " + B + "🚀 Scaffolding project into " + targetDir + " using " + pm + "..." + R + "\n");
167
262
 
168
263
  try {
169
264
  if (isNuxt) {
170
- execSync('npx --yes nuxi@latest init "' + targetDir + '" --packageManager npm', { stdio: 'inherit' });
265
+ execSync(npxCmd + ' nuxi@latest init "' + targetDir + '" --packageManager ' + pm, { stdio: 'inherit' });
171
266
  } else if (isNext) {
172
- execSync('npx --yes create-next-app@latest "' + targetDir + '" --js --eslint --tailwind --app --src-dir --import-alias "@/*" --use-npm', { stdio: 'inherit' });
267
+ execSync(npxCmd + ' create-next-app@latest "' + targetDir + '" --js --eslint --tailwind --app --src-dir --import-alias "@/*" --use-' + pm, { stdio: 'inherit' });
173
268
  } else if (isVite) {
174
- execSync('npm create vite@latest "' + targetDir + '" -- --template react', { stdio: 'inherit' });
269
+ const createCmd = pm === 'npm' ? 'npm create vite@latest' : pm === 'yarn' ? 'yarn create vite' : pm === 'pnpm' ? 'pnpm create vite' : 'bun create vite';
270
+ execSync(createCmd + ' "' + targetDir + '" -- --template react', { stdio: 'inherit' });
175
271
  }
176
272
 
177
273
  console.log("\n " + B + "📦 Installing " + C.cyan + "tiktok-live-api" + B + " SDK..." + R + "\n");
178
- execSync('npm install tiktok-live-api', { cwd: fullPath, stdio: 'inherit' });
274
+ execSync(installCmd, { cwd: fullPath, stdio: 'inherit' });
179
275
 
180
276
  if (isNuxt) {
181
277
  const compDir = path.join(fullPath, 'components');
@@ -186,9 +282,9 @@ function runScaffold(choice, targetDir) {
186
282
  if (fs.existsSync(appVue)) {
187
283
  fs.writeFileSync(appVue, [
188
284
  '<template>',
189
- ' <div style="min-height: 100vh; background: #fdfdfd; padding-top: 40px;">',
285
+ ' <main style="min-height: 100vh; background: #f9fafb; padding-top: 40px;">',
190
286
  ' <TikTokLive />',
191
- ' </div>',
287
+ ' </main>',
192
288
  '</template>'
193
289
  ].join('\n') + '\n');
194
290
  }
@@ -204,7 +300,7 @@ function runScaffold(choice, targetDir) {
204
300
  "",
205
301
  "export default function Home() {",
206
302
  " return (",
207
- ' <main className="min-h-screen bg-neutral-50 pt-10">',
303
+ ' <main style={{ minHeight: "100vh", background: "#f9fafb", paddingTop: "40px" }}>',
208
304
  " <TikTokLive />",
209
305
  " </main>",
210
306
  " );",
@@ -223,9 +319,9 @@ function runScaffold(choice, targetDir) {
223
319
  "",
224
320
  "export default function App() {",
225
321
  " return (",
226
- " <div style={{ minHeight: '100vh', background: '#fdfdfd', paddingTop: '40px' }}>",
322
+ ' <main style={{ minHeight: "100vh", background: "#f9fafb", paddingTop: "40px" }}>',
227
323
  " <TikTokLive />",
228
- " </div>",
324
+ " </main>",
229
325
  " );",
230
326
  "}"
231
327
  ].join('\n') + '\n');
@@ -235,7 +331,10 @@ function runScaffold(choice, targetDir) {
235
331
  console.log("\n " + C.green + "✔ Project successfully scaffolded!" + R + "\n");
236
332
  console.log(" " + B + "Next steps:" + R);
237
333
  console.log(" cd " + targetDir);
238
- console.log(" npm run dev\n");
334
+ if (isVite && pm !== 'bun') {
335
+ console.log(" " + pm + " install");
336
+ }
337
+ console.log(" " + devCmd + "\n");
239
338
 
240
339
  } catch (err) {
241
340
  console.error("\n " + C.red + "Error during scaffolding:" + R, err.message);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tiktok-live-api",
3
- "version": "1.2.4",
3
+ "version": "1.2.5",
4
4
  "description": "Unofficial TikTok LIVE API Client — Real-time chat, gifts, viewers, battles, and AI live captions from any TikTok livestream. Managed WebSocket API with 99.9% uptime.",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",