tiktok-live-api 1.2.13 → 1.2.16
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.
- package/bin/demo.mjs +103 -89
- package/package.json +1 -1
package/bin/demo.mjs
CHANGED
|
@@ -11,75 +11,82 @@ import path from 'path';
|
|
|
11
11
|
const REACT_TEMPLATE = [
|
|
12
12
|
'"use client";',
|
|
13
13
|
"import React, { useEffect, useState, useRef } from 'react';",
|
|
14
|
-
"import { TikTokLive } from 'tiktok-live-api';",
|
|
15
14
|
"",
|
|
16
15
|
"export default function TikTokLiveComponent() {",
|
|
17
16
|
" const [events, setEvents] = useState([]);",
|
|
18
|
-
" const [username, setUsername] = useState('gbnews');",
|
|
19
|
-
" const [apiKey, setApiKey] = useState('demo_tiktokliveapi_public_2026');",
|
|
20
17
|
" const [inputUsername, setInputUsername] = useState('gbnews');",
|
|
18
|
+
" const [apiKey, setApiKey] = useState('demo_tiktokliveapi_public_2026');",
|
|
21
19
|
" const [connected, setConnected] = useState(false);",
|
|
22
|
-
" const
|
|
20
|
+
" const wsRef = useRef(null);",
|
|
23
21
|
"",
|
|
24
|
-
"
|
|
25
|
-
"
|
|
26
|
-
"
|
|
22
|
+
" function addEvent(str) {",
|
|
23
|
+
" setEvents(prev => [...prev.slice(-50), str]);",
|
|
24
|
+
" }",
|
|
25
|
+
"",
|
|
26
|
+
" function connectStream(e) {",
|
|
27
|
+
" if (e) e.preventDefault();",
|
|
28
|
+
" if (wsRef.current) { wsRef.current.close(); wsRef.current = null; }",
|
|
27
29
|
" setEvents([]);",
|
|
28
30
|
" setConnected(false);",
|
|
29
|
-
" ",
|
|
30
|
-
"
|
|
31
|
-
"
|
|
32
|
-
"
|
|
33
|
-
"
|
|
34
|
-
"
|
|
35
|
-
"
|
|
36
|
-
"
|
|
37
|
-
"
|
|
38
|
-
"
|
|
39
|
-
"
|
|
40
|
-
"
|
|
41
|
-
"
|
|
42
|
-
"
|
|
43
|
-
"
|
|
31
|
+
" const user = (inputUsername || '').replace(/^@/, '');",
|
|
32
|
+
" if (!user) return;",
|
|
33
|
+
" try {",
|
|
34
|
+
" const ws = new WebSocket('wss://api.tik.tools?uniqueId=' + user + '&apiKey=' + apiKey);",
|
|
35
|
+
" wsRef.current = ws;",
|
|
36
|
+
" ws.onopen = () => { setConnected(true); addEvent('Connected to @' + user); };",
|
|
37
|
+
" ws.onclose = () => { setConnected(false); wsRef.current = null; };",
|
|
38
|
+
" ws.onerror = () => { addEvent('WebSocket error'); };",
|
|
39
|
+
" ws.onmessage = (evt) => {",
|
|
40
|
+
" try {",
|
|
41
|
+
" const msg = JSON.parse(evt.data);",
|
|
42
|
+
" const t = msg.event || '';",
|
|
43
|
+
" const d = msg.data || {};",
|
|
44
|
+
" const u = d.user ? d.user.uniqueId : '';",
|
|
45
|
+
" if (t === 'chat') addEvent('[chat] ' + u + ': ' + d.comment);",
|
|
46
|
+
" else if (t === 'gift') addEvent('[gift] ' + u + ' sent ' + (d.giftName || 'a gift'));",
|
|
47
|
+
" else if (t === 'like') addEvent('[like] ' + u + ' liked');",
|
|
48
|
+
" else if (t === 'member') addEvent('[join] ' + u + ' joined');",
|
|
49
|
+
" else if (t === 'roomUserSeq') addEvent('[viewers] ' + (d.viewerCount || 0));",
|
|
50
|
+
" } catch (err) {}",
|
|
51
|
+
" };",
|
|
52
|
+
" } catch (err) { addEvent('Connection failed: ' + err.message); }",
|
|
53
|
+
" }",
|
|
44
54
|
"",
|
|
45
|
-
"
|
|
46
|
-
" e.preventDefault();",
|
|
47
|
-
" setUsername(inputUsername);",
|
|
48
|
-
" };",
|
|
55
|
+
" useEffect(() => { return () => { if (wsRef.current) wsRef.current.close(); }; }, []);",
|
|
49
56
|
"",
|
|
50
57
|
" return (",
|
|
51
58
|
" <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
59
|
" <div style={{ padding: '24px', borderBottom: '1px solid #e5e7eb' }}>",
|
|
53
|
-
" <h2 style={{ margin: '0 0 8px 0', fontSize: '1.5rem', fontWeight:
|
|
54
|
-
" <p style={{ margin: 0, fontSize: '0.875rem', color: '#
|
|
55
|
-
|
|
60
|
+
" <h2 style={{ margin: '0 0 8px 0', fontSize: '1.5rem', fontWeight: 700, color: '#111827' }}>TikTok Live SDK</h2>",
|
|
61
|
+
" <p style={{ margin: 0, fontSize: '0.875rem', color: '#4b5563' }}>",
|
|
62
|
+
' Get your own API key at <a href="https://tik.tools" target="_blank" style={{ color: \'#3b82f6\', textDecoration: \'none\' }}>tik.tools</a>',
|
|
56
63
|
" </p>",
|
|
57
64
|
" </div>",
|
|
58
65
|
" <div style={{ padding: '24px' }}>",
|
|
59
|
-
" <form onSubmit={
|
|
66
|
+
" <form onSubmit={connectStream} style={{ display: 'flex', flexDirection: 'column', gap: '16px', marginBottom: '24px' }}>",
|
|
60
67
|
" <div>",
|
|
61
|
-
" <label style={{ display: 'block', fontSize: '0.875rem', fontWeight:
|
|
62
|
-
|
|
68
|
+
" <label style={{ display: 'block', fontSize: '0.875rem', fontWeight: 600, marginBottom: '6px', color: '#111827' }}>TikTok Username</label>",
|
|
69
|
+
' <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\', color: \'#111827\' }} placeholder="e.g. gbnews" />',
|
|
63
70
|
" </div>",
|
|
64
71
|
" <div>",
|
|
65
|
-
" <label style={{ display: 'block', fontSize: '0.875rem', fontWeight:
|
|
66
|
-
|
|
67
|
-
" <p style={{ margin: '4px 0 0 0', fontSize: '0.75rem', color: '#
|
|
72
|
+
" <label style={{ display: 'block', fontSize: '0.875rem', fontWeight: 600, marginBottom: '6px', color: '#111827' }}>API Key</label>",
|
|
73
|
+
' <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\', color: \'#111827\' }} />',
|
|
74
|
+
" <p style={{ margin: '4px 0 0 0', fontSize: '0.75rem', color: '#6b7280' }}>The demo key is rate-limited. Replace with your own key for production.</p>",
|
|
68
75
|
" </div>",
|
|
69
|
-
|
|
76
|
+
' <button type="submit" style={{ background: \'#111827\', color: \'#fff\', padding: \'10px 16px\', borderRadius: \'6px\', border: \'none\', fontWeight: 600, cursor: \'pointer\', fontSize: \'0.875rem\' }}>Connect Stream</button>',
|
|
70
77
|
" </form>",
|
|
71
78
|
" <div style={{ marginBottom: '16px', display: 'flex', alignItems: 'center', gap: '8px' }}>",
|
|
72
79
|
" <div style={{ width: 10, height: 10, borderRadius: '50%', background: connected ? '#10b981' : '#ef4444', transition: 'background 0.3s' }}></div>",
|
|
73
|
-
" <span style={{ fontSize: '0.875rem', color: '#
|
|
80
|
+
" <span style={{ fontSize: '0.875rem', color: '#111827', fontWeight: 600 }}>{connected ? 'Connected' : 'Disconnected'}</span>",
|
|
74
81
|
" </div>",
|
|
75
82
|
" <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
|
|
83
|
+
" {events.map((e, i) => <div key={i} style={{ padding: '6px 0', borderBottom: '1px solid #f3f4f6', wordBreak: 'break-word', color: '#374151' }}>{e}</div>)}",
|
|
84
|
+
" {events.length === 0 && <div style={{ color: '#9ca3af', textAlign: 'center', margin: 'auto 0' }}>{connected ? 'Waiting for events...' : 'Connect to see events'}</div>}",
|
|
78
85
|
" </div>",
|
|
79
86
|
" </div>",
|
|
80
87
|
" </div>",
|
|
81
88
|
" );",
|
|
82
|
-
"}"
|
|
89
|
+
"}",
|
|
83
90
|
].join('\n') + "\n";
|
|
84
91
|
|
|
85
92
|
const VUE_TEMPLATE = [
|
|
@@ -368,7 +375,7 @@ function runScaffold(choice, targetDir, pm) {
|
|
|
368
375
|
'import json',
|
|
369
376
|
'import websockets',
|
|
370
377
|
'',
|
|
371
|
-
'#
|
|
378
|
+
'# \u2500\u2500 Configuration \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500',
|
|
372
379
|
"USERNAME = 'gbnews' # TikTok @username",
|
|
373
380
|
"API_KEY = 'demo_tiktokliveapi_public_2026' # Get yours at https://tik.tools",
|
|
374
381
|
'',
|
|
@@ -376,32 +383,40 @@ function runScaffold(choice, targetDir, pm) {
|
|
|
376
383
|
'async def main():',
|
|
377
384
|
' """Connect to TikTok LIVE and print events."""',
|
|
378
385
|
' uri = f"wss://api.tik.tools?uniqueId={USERNAME}&apiKey={API_KEY}"',
|
|
379
|
-
' print(f"
|
|
386
|
+
' print(f"Connecting to @{USERNAME}...")',
|
|
380
387
|
'',
|
|
381
388
|
' try:',
|
|
382
389
|
' async with websockets.connect(uri) as ws:',
|
|
383
|
-
' print(f"
|
|
390
|
+
' print(f"Connected to @{USERNAME}")',
|
|
391
|
+
' print()',
|
|
384
392
|
' async for raw in ws:',
|
|
385
393
|
' try:',
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
394
|
+
" msg = json.loads(raw)",
|
|
395
|
+
" event = msg.get('event', '')",
|
|
396
|
+
" data = msg.get('data', {})",
|
|
397
|
+
" user = data.get('user', {}).get('uniqueId', '?')",
|
|
390
398
|
'',
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
'
|
|
394
|
-
|
|
395
|
-
'
|
|
396
|
-
|
|
397
|
-
'
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
' print(f"
|
|
399
|
+
" if event == 'chat':",
|
|
400
|
+
" comment = data.get('comment', '')",
|
|
401
|
+
' print(f"[chat] {user}: {comment}")',
|
|
402
|
+
" elif event == 'gift':",
|
|
403
|
+
" gift = data.get('giftName', 'a gift')",
|
|
404
|
+
" count = data.get('repeatCount', 1)",
|
|
405
|
+
' print(f"[gift] {user} sent {gift} x{count}")',
|
|
406
|
+
" elif event == 'like':",
|
|
407
|
+
" total = data.get('totalLikeCount', 0)",
|
|
408
|
+
' print(f"[like] {user} liked (total: {total})")',
|
|
409
|
+
" elif event == 'member':",
|
|
410
|
+
' print(f"[join] {user} joined")',
|
|
411
|
+
" elif event == 'roomUserSeq':",
|
|
412
|
+
" viewers = data.get('viewerCount', 0)",
|
|
413
|
+
' print(f"[viewers] {viewers}")',
|
|
401
414
|
' except json.JSONDecodeError:',
|
|
402
415
|
' pass',
|
|
416
|
+
' except KeyboardInterrupt:',
|
|
417
|
+
' print("Disconnected.")',
|
|
403
418
|
' except Exception as e:',
|
|
404
|
-
' print(f"
|
|
419
|
+
' print(f"Error: {e}")',
|
|
405
420
|
'',
|
|
406
421
|
'',
|
|
407
422
|
'if __name__ == "__main__":',
|
|
@@ -458,47 +473,46 @@ function runScaffold(choice, targetDir, pm) {
|
|
|
458
473
|
'</template>'
|
|
459
474
|
].join('\n') + '\n');
|
|
460
475
|
} else {
|
|
461
|
-
console.log("\n " + B + "📦 Installing " + C.cyan + "tiktok-live-api" + B + " SDK..." + R + "\n");
|
|
462
|
-
execSync(installCmd, { cwd: fullPath, stdio: 'inherit' });
|
|
463
|
-
|
|
464
476
|
if (isNext) {
|
|
465
477
|
const compDir = path.join(fullPath, 'src', 'components');
|
|
466
478
|
if (!fs.existsSync(compDir)) fs.mkdirSync(compDir, { recursive: true });
|
|
467
479
|
fs.writeFileSync(path.join(compDir, 'TikTokLive.jsx'), REACT_TEMPLATE);
|
|
468
480
|
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
481
|
+
// Write page.js — create the directory if needed
|
|
482
|
+
const pageDir = path.join(fullPath, 'src', 'app');
|
|
483
|
+
if (!fs.existsSync(pageDir)) fs.mkdirSync(pageDir, { recursive: true });
|
|
484
|
+
fs.writeFileSync(path.join(pageDir, 'page.js'), [
|
|
485
|
+
"import TikTokLive from '@/components/TikTokLive';",
|
|
486
|
+
"",
|
|
487
|
+
"export default function Home() {",
|
|
488
|
+
" return (",
|
|
489
|
+
' <main style={{ minHeight: "100vh", background: "#f9fafb", paddingTop: "40px" }}>',
|
|
490
|
+
" <TikTokLive />",
|
|
491
|
+
" </main>",
|
|
492
|
+
" );",
|
|
493
|
+
"}",
|
|
494
|
+
].join('\n') + '\n');
|
|
483
495
|
} else if (isVite) {
|
|
484
496
|
const compDir = path.join(fullPath, 'src', 'components');
|
|
485
497
|
if (!fs.existsSync(compDir)) fs.mkdirSync(compDir, { recursive: true });
|
|
486
498
|
fs.writeFileSync(path.join(compDir, 'TikTokLive.jsx'), REACT_TEMPLATE);
|
|
487
499
|
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
500
|
+
// Write App.jsx — overwrite whatever create-vite generated (could be .jsx or .tsx)
|
|
501
|
+
const appContent = [
|
|
502
|
+
"import TikTokLive from './components/TikTokLive';",
|
|
503
|
+
"",
|
|
504
|
+
"export default function App() {",
|
|
505
|
+
" return (",
|
|
506
|
+
' <main style={{ minHeight: "100vh", background: "#f9fafb", paddingTop: "40px" }}>',
|
|
507
|
+
" <TikTokLive />",
|
|
508
|
+
" </main>",
|
|
509
|
+
" );",
|
|
510
|
+
"}",
|
|
511
|
+
].join('\n') + '\n';
|
|
512
|
+
fs.writeFileSync(path.join(fullPath, 'src', 'App.jsx'), appContent);
|
|
513
|
+
// Also remove App.tsx if it exists (create-vite sometimes uses TypeScript)
|
|
514
|
+
const appTsx = path.join(fullPath, 'src', 'App.tsx');
|
|
515
|
+
if (fs.existsSync(appTsx)) fs.unlinkSync(appTsx);
|
|
502
516
|
}
|
|
503
517
|
}
|
|
504
518
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tiktok-live-api",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.16",
|
|
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",
|