tiktok-live-api 1.2.15 → 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.
Files changed (2) hide show
  1. package/bin/demo.mjs +77 -71
  2. 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 clientRef = useRef(null);",
20
+ " const wsRef = useRef(null);",
23
21
  "",
24
- " useEffect(() => {",
25
- " if (!username) return;",
26
- " if (clientRef.current) { clientRef.current.disconnect(); }",
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
- " 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\"]));",
37
- " client.on('connected', () => setConnected(true));",
38
- " client.on('disconnected', () => setConnected(false));",
39
- " ",
40
- " client.connect().catch(() => setConnected(false));",
41
- " ",
42
- " return () => { if (clientRef.current) { clientRef.current.disconnect(); } };",
43
- " }, [username, apiKey]);",
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
- " const handleConnect = (e) => {",
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: 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>",
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={handleConnect} style={{ display: 'flex', flexDirection: 'column', gap: '16px', marginBottom: '24px' }}>",
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: 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. gbnews\" />",
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: 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>",
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
- " <button type=\"submit\" style={{ background: '#000', color: '#fff', padding: '10px 16px', borderRadius: '6px', border: 'none', fontWeight: 500, cursor: 'pointer' }}>Connect Stream</button>",
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: '#374151', fontWeight: 500 }}>{connected ? 'Connected to @' + username : 'Disconnected'}</span>",
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 a valid stream to see events'}</div>}",
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 = [
@@ -466,47 +473,46 @@ function runScaffold(choice, targetDir, pm) {
466
473
  '</template>'
467
474
  ].join('\n') + '\n');
468
475
  } else {
469
- console.log("\n " + B + "📦 Installing " + C.cyan + "tiktok-live-api" + B + " SDK..." + R + "\n");
470
- execSync(installCmd, { cwd: fullPath, stdio: 'inherit' });
471
-
472
476
  if (isNext) {
473
477
  const compDir = path.join(fullPath, 'src', 'components');
474
478
  if (!fs.existsSync(compDir)) fs.mkdirSync(compDir, { recursive: true });
475
479
  fs.writeFileSync(path.join(compDir, 'TikTokLive.jsx'), REACT_TEMPLATE);
476
480
 
477
- const pageJs = path.join(fullPath, 'src', 'app', 'page.js');
478
- if (fs.existsSync(pageJs)) {
479
- fs.writeFileSync(pageJs, [
480
- "import TikTokLive from '@/components/TikTokLive';",
481
- "",
482
- "export default function Home() {",
483
- " return (",
484
- ' <main style={{ minHeight: "100vh", background: "#f9fafb", paddingTop: "40px" }}>',
485
- " <TikTokLive />",
486
- " </main>",
487
- " );",
488
- "}"
489
- ].join('\n') + '\n');
490
- }
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');
491
495
  } else if (isVite) {
492
496
  const compDir = path.join(fullPath, 'src', 'components');
493
497
  if (!fs.existsSync(compDir)) fs.mkdirSync(compDir, { recursive: true });
494
498
  fs.writeFileSync(path.join(compDir, 'TikTokLive.jsx'), REACT_TEMPLATE);
495
499
 
496
- const appJsx = path.join(fullPath, 'src', 'App.jsx');
497
- if (fs.existsSync(appJsx)) {
498
- fs.writeFileSync(appJsx, [
499
- "import TikTokLive from './components/TikTokLive';",
500
- "",
501
- "export default function App() {",
502
- " return (",
503
- ' <main style={{ minHeight: "100vh", background: "#f9fafb", paddingTop: "40px" }}>',
504
- " <TikTokLive />",
505
- " </main>",
506
- " );",
507
- "}"
508
- ].join('\n') + '\n');
509
- }
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);
510
516
  }
511
517
  }
512
518
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tiktok-live-api",
3
- "version": "1.2.15",
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",