LibreSpark/LibreTV
一分钟搭建影视站,支持Docker等部署方式
TypeScript★ 18,227+978 that day#4 on trending
B75.9/100
Duplication91
0.9% in typescript, tsx, bash, perl, javascriptDead code96
0.2%Complexity50
52.1% The score is jscpd's own --health model: markup, data and text left out, tests kept. The numbers below are the trending scan's.
0.93%duplicated code · 163 of 17,617 lines
16clones in code
105code files of 141 scanned
1.02%duplicated over all files · 24 clones
Most complex files
- src/components/source-manager.tsx290 cx
- src/lib/store.ts239 cx
- src/components/live-channel-list.tsx156 cx
- src/components/player-shell.tsx120 cx
- src/app/api/live/probe/route.ts119 cx
- src/lib/tvbox-parser.ts114 cx
- src/app/live/page.tsx113 cx
- src/lib/m3u8.ts110 cx
- src/app/watch/page.tsx105 cx
- src/components/live-player.tsx100 cx
Dead code 0.16% unused · 4 findings
- src/lib/video-cache.ts:hasCachedSegmentunused-export
- src/lib/video-cache.ts:deleteEpisodeCacheunused-export
- src/lib/fetch-utils.ts:fetchUpstreamJsonunused-export
- src/lib/client-api.ts:SearchFailureunused-export
The code number leaves out tests, fixtures, docs, examples, data files, styles, translations and vendored code.
Largest clones in the code
- 15 lines in 3 placestypescript78 tokens
85export function stripLeadAdGroup(m3u8Content: string): string { 86 if (!m3u8Content) return ''; 87 const lines = m3u8Content.split('\n'); 88 const isDisc = (l: string) => l.trim() === '#EXT-X-DISCONTINUITY'; 89 const isSegment = (l: string) => { 90 const t = l.trim(); 91 return t !== '' && !t.startsWith('#'); 92 }; 93 94 // 多分组封装(采集站把流按固定块切分、每个衔接处都打 DISCONTINUITY)不是广告。169function stripMarkedAdGroups(m3u8Content: string): string { 170 if (!m3u8Content) return ''; 171 const lines = m3u8Content.split('\n'); 172 const isDisc = (l: string) => l.trim() === '#EXT-X-DISCONTINUITY'; 173 const isSegment = (l: string) => { 174 const t = l.trim(); 175 return t !== '' && !t.startsWith('#'); 176 }; 177 178 const drop = new Array<boolean>(lines.length).fill(false);First 10 of 15 lines, the second copy on the right.
- 19 lines in 2 placestsx110 tokens
120 {name} 121 </ChipButton> 122 ))} 123</div> 124 125{query.isError ? ( 126 <p className="text-center text-sm text-faint py-8">推荐内容加载失败,可稍后重试或在设置中关闭</p> 127) : query.isLoading ? ( 128 <GridSkeleton /> 129) : (First 10 of 19 lines.
- 10 lines in 2 placestypescript107 tokens
231for (let i = 1; i <= total; i++) { 232 if (signal.aborted) return; 233 await pause.waitIfPaused(); 234 const resp = await cache.match(new Request(chunkKey(i))); 235 if (!resp) throw new Error(`分片 ${i} 缓存丢失,请重试`); 236 let data = await resp.arrayBuffer(); 237 if (aesKey && parsed.aesConf) { 238 data = await decryptSegment(data, aesKey, parsed.aesConf.iv, parsed.mediaSequence, i - 1); 239 } 240 await transmuxer.pushAndTransmux(new Uint8Array(data));244for (let i = 1; i <= total; i++) { 245 if (signal.aborted) return; 246 await pause.waitIfPaused(); 247 const resp = await cache.match(new Request(chunkKey(i))); 248 if (!resp) throw new Error(`分片 ${i} 缓存丢失,请重试`); 249 let data = await resp.arrayBuffer(); 250 if (aesKey && parsed.aesConf) { 251 data = await decryptSegment(data, aesKey, parsed.aesConf.iv, parsed.mediaSequence, i - 1); 252 } 253 await target.write(new Uint8Array(data));
7 more
- 6 lines in 3 placestypescript51 tokens
- 14 lines in 2 placestsx81 tokens
- 9 lines in 2 placestypescript74 tokens
- 11 lines in 2 placestsx68 tokens
- 6 lines in 2 placestsx67 tokens
- 7 lines in 2 placestsx61 tokens
- 10 lines in 2 placestsx60 tokens
Largest clones over all files, tests and data included
- 15 lines in 3 placestypescript78 tokens
85export function stripLeadAdGroup(m3u8Content: string): string { 86 if (!m3u8Content) return ''; 87 const lines = m3u8Content.split('\n'); 88 const isDisc = (l: string) => l.trim() === '#EXT-X-DISCONTINUITY'; 89 const isSegment = (l: string) => { 90 const t = l.trim(); 91 return t !== '' && !t.startsWith('#'); 92 }; 93 94 // 多分组封装(采集站把流按固定块切分、每个衔接处都打 DISCONTINUITY)不是广告。169function stripMarkedAdGroups(m3u8Content: string): string { 170 if (!m3u8Content) return ''; 171 const lines = m3u8Content.split('\n'); 172 const isDisc = (l: string) => l.trim() === '#EXT-X-DISCONTINUITY'; 173 const isSegment = (l: string) => { 174 const t = l.trim(); 175 return t !== '' && !t.startsWith('#'); 176 }; 177 178 const drop = new Array<boolean>(lines.length).fill(false);First 10 of 15 lines, the second copy on the right.
- 19 lines in 2 placestsx110 tokens
120 {name} 121 </ChipButton> 122 ))} 123</div> 124 125{query.isError ? ( 126 <p className="text-center text-sm text-faint py-8">推荐内容加载失败,可稍后重试或在设置中关闭</p> 127) : query.isLoading ? ( 128 <GridSkeleton /> 129) : (First 10 of 19 lines.
- 10 lines in 2 placestypescript107 tokens
231for (let i = 1; i <= total; i++) { 232 if (signal.aborted) return; 233 await pause.waitIfPaused(); 234 const resp = await cache.match(new Request(chunkKey(i))); 235 if (!resp) throw new Error(`分片 ${i} 缓存丢失,请重试`); 236 let data = await resp.arrayBuffer(); 237 if (aesKey && parsed.aesConf) { 238 data = await decryptSegment(data, aesKey, parsed.aesConf.iv, parsed.mediaSequence, i - 1); 239 } 240 await transmuxer.pushAndTransmux(new Uint8Array(data));244for (let i = 1; i <= total; i++) { 245 if (signal.aborted) return; 246 await pause.waitIfPaused(); 247 const resp = await cache.match(new Request(chunkKey(i))); 248 if (!resp) throw new Error(`分片 ${i} 缓存丢失,请重试`); 249 let data = await resp.arrayBuffer(); 250 if (aesKey && parsed.aesConf) { 251 data = await decryptSegment(data, aesKey, parsed.aesConf.iv, parsed.mediaSequence, i - 1); 252 } 253 await target.write(new Uint8Array(data)); - 6 lines in 2 placescssstyles103 tokens
- 6 lines in 3 placestypescript51 tokens
By format, code only
| Format | Files | Lines | Clones | Duplicated lines | Duplication |
|---|---|---|---|---|---|
| tsx | 33 | 8,410 | 8 | 90 | 1.07% |
| typescript | 61 | 8,319 | 8 | 73 | 0.88% |
| bash | 9 | 694 | 0 | 0 | 0% |
| perl | 1 | 168 | 0 | 0 | 0% |
| javascript | 1 | 26 | 0 | 0 | 0% |
By format, all files
| Format | Files | Lines | Clones | Duplicated lines | Duplication |
|---|---|---|---|---|---|
| typescript | 89 | 12,122 | 12 | 103 | 0.85% |
| tsx | 33 | 8,410 | 8 | 90 | 1.07% |
| yaml | 3 | 345 | 3 | 28 | 8.12% |
| css | 1 | 156 | 1 | 6 | 3.85% |
| bash | 10 | 710 | 0 | 0 | 0% |
| markdown | 1 | 246 | 0 | 0 | 0% |
| perl | 1 | 168 | 0 | 0 | 0% |
| json | 2 | 62 | 0 | 0 | 0% |
| javascript | 1 | 26 | 0 | 0 | 0% |
Trending appearances
| Day | Rank | Stars | Files | Lines | Clones | All files | Code | Health | Commit |
|---|---|---|---|---|---|---|---|---|---|
| Oct 16, 2026 | #4 | 18,227 (+978) | 141 | 22,245 | 24 | 1.02% | 0.93% | B 75.9 | a24ad33 |
Want this for your own project? Install jscpd and run jscpd ., or measure it the way the baselines were measured.