ckplayer-plus 1.1.1 → 1.1.3
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/README.md +3 -3
- package/dist/ckplayer.js +136 -48
- package/dist/ckplayer.min.js +2 -2
- package/dist/index.cjs +113 -40
- package/dist/index.js +113 -40
- package/dist/react.cjs +116 -40
- package/dist/react.js +116 -40
- package/dist/vue.cjs +116 -40
- package/dist/vue.js +116 -40
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -63,7 +63,7 @@ React:
|
|
|
63
63
|
| `container` | 核心 API 必须;组件不用 | — | 播放器挂载节点。Vue/React 组件内部自动创建,**不要传** |
|
|
64
64
|
| `video` | 必须 | — | 视频地址或清晰度数组 |
|
|
65
65
|
| `poster` | 可选 | `''` | 封面图 |
|
|
66
|
-
| `autoplay` | 可选 | `false` |
|
|
66
|
+
| `autoplay` | 可选 | `false` | 自动播放。**课程/断点续播请保持 `false`**:有 `seek` 时组件会强制关闭自动播,只定位进度,需用户点击播放 |
|
|
67
67
|
| `volume` | 可选 | `0.8` | 音量 0–1 |
|
|
68
68
|
| `live` | 可选 | `false` | 直播相关 |
|
|
69
69
|
| `width` / `height` | 组件可选 | `100%` / `400px` | 仅 Vue/React 组件样式 |
|
|
@@ -77,7 +77,7 @@ React:
|
|
|
77
77
|
| `preferBuiltinPlug` | `true` | 优先加载本包内置脚本,避免复用页面上其它全局 `Hls` |
|
|
78
78
|
| `cdnFallback` | `true` | `getPath` 失败时回退 jsDelivr;内网无外网请设 `false` 并配 `pluginPath` |
|
|
79
79
|
| `pluginPath` | 自动 | 插件脚本根路径。默认:`window.ckplayerBasePath` → 本包 `dist/` → jsDelivr CDN |
|
|
80
|
-
| `seek` | `0` |
|
|
80
|
+
| `seek` | `0` | 续播秒数。HLS 对齐分片起点缓冲后再定位;**只 seek 不自动 play**,避免 Network 出现 canceled `.ts` |
|
|
81
81
|
| `loaded` | — | **只支持函数**;字符串形式已禁用(防 XSS) |
|
|
82
82
|
|
|
83
83
|
### 进阶(按需)
|
|
@@ -125,7 +125,7 @@ export default {
|
|
|
125
125
|
</script>
|
|
126
126
|
```
|
|
127
127
|
|
|
128
|
-
断点续播:`seek` 用 `:seek` 或 `options.seek` 在创建时传入;**不要**在 `@ready` 里再 `player.seek(16)`,否则会重复加载 HLS、取消 `.ts`
|
|
128
|
+
断点续播:`seek` 用 `:seek` 或 `options.seek` 在创建时传入;**不要**在 `@ready` 里再 `player.seek(16)`,否则会重复加载 HLS、取消 `.ts` 分片导致卡住。有 `seek` 时不会自动播放,定位完成后点中央播放按钮即可。切集/切换清晰度同样不强制自动播。
|
|
129
129
|
|
|
130
130
|
SSR(Nuxt 等)请用 `<ClientOnly>`,仅在浏览器初始化。
|
|
131
131
|
|
package/dist/ckplayer.js
CHANGED
|
@@ -141,7 +141,7 @@
|
|
|
141
141
|
cdnFallback:true// getPath 失败时是否回退到 jsDelivr(内网可设 false,并配置 pluginPath)
|
|
142
142
|
};
|
|
143
143
|
// 打包时替换为真实版本;用于 bundler 下 getPath 失效时的 CDN 回退
|
|
144
|
-
var ckplayerPlusDefaultCdn='https://cdn.jsdelivr.net/npm/ckplayer-plus@1.1.
|
|
144
|
+
var ckplayerPlusDefaultCdn='https://cdn.jsdelivr.net/npm/ckplayer-plus@1.1.3/dist/';
|
|
145
145
|
function ckplayerEmbed(videoObj){
|
|
146
146
|
/*
|
|
147
147
|
* rightMenu
|
|
@@ -446,7 +446,7 @@
|
|
|
446
446
|
}
|
|
447
447
|
var allowCdn=!vars || vars['cdnFallback']!==false;
|
|
448
448
|
var cdn=(typeof window!=='undefined' && window.ckplayerPlusCdn) ? window.ckplayerPlusCdn : ckplayerPlusDefaultCdn;
|
|
449
|
-
if(!allowCdn || !cdn || cdn.indexOf('1.1.
|
|
449
|
+
if(!allowCdn || !cdn || cdn.indexOf('1.1.3')>-1){
|
|
450
450
|
// 避免占位版本或显式关闭 CDN 时请求坏地址;交给相对路径或业务 pluginPath
|
|
451
451
|
return folder+'/'+file;
|
|
452
452
|
}
|
|
@@ -594,7 +594,13 @@
|
|
|
594
594
|
// 不使用原生 autoplay 属性:浏览器会在 seek 前开始 play,随后
|
|
595
595
|
// currentTime / pause 等初始化动作会打断其 Promise,导致永久 loading。
|
|
596
596
|
video.attr('preload','auto');
|
|
597
|
-
|
|
597
|
+
// 带断点续播时不自动播:只定位进度,等用户点击(避免 .ts canceled)
|
|
598
|
+
if(parseResumeSeekFromVars()>0){
|
|
599
|
+
autoplayPending=false;
|
|
600
|
+
}
|
|
601
|
+
else{
|
|
602
|
+
autoplayPending=true;
|
|
603
|
+
}
|
|
598
604
|
}
|
|
599
605
|
else{
|
|
600
606
|
video.attr('preload','metadata');
|
|
@@ -763,6 +769,10 @@
|
|
|
763
769
|
if(!seekTime){
|
|
764
770
|
applyResumeSeekState(parseResumeSeekFromVars());
|
|
765
771
|
}
|
|
772
|
+
// 有续播目标:取消自动播放,定位后展示中央播放按钮
|
|
773
|
+
if(seekTime>0){
|
|
774
|
+
autoplayPending=false;
|
|
775
|
+
}
|
|
766
776
|
},
|
|
767
777
|
/*
|
|
768
778
|
* hlsSeekTo
|
|
@@ -990,10 +1000,8 @@
|
|
|
990
1000
|
if(!resumeAt){
|
|
991
1001
|
resumeAt=parseResumeSeekFromVars();
|
|
992
1002
|
}
|
|
993
|
-
//
|
|
994
|
-
//
|
|
995
|
-
// 1) 从目标前约一个分片开始加载,让前序关键帧进入缓冲
|
|
996
|
-
// 2) 等 hlsCanSafelySeekTo 后再写 currentTime
|
|
1003
|
+
// HLS 续播:不要用精确秒数做 startPosition(易在分片中部起播导致解码失败/永久 loading)。
|
|
1004
|
+
// 对齐目标分片起点加载,等分片从起点缓冲到目标后再一次 currentTime,并强制 play。
|
|
997
1005
|
if(resumeAt && resumeAt>0 && (isUndefined(vars['live']) || !vars['live'])){
|
|
998
1006
|
maxSeeTime=Math.max(maxSeeTime,resumeAt);
|
|
999
1007
|
if(firstSeekTime<0){
|
|
@@ -1002,16 +1010,28 @@
|
|
|
1002
1010
|
seekTime=resumeAt;
|
|
1003
1011
|
hlsResumeTarget=resumeAt;
|
|
1004
1012
|
hlsResumeWarmup=true;
|
|
1005
|
-
hlsConf.startPosition=hlsPriorLoadTime(resumeAt);
|
|
1006
1013
|
}
|
|
1007
1014
|
hls = new Hls(hlsConf);
|
|
1008
1015
|
hls.loadSource(url);
|
|
1009
1016
|
hls.attachMedia(video);
|
|
1010
1017
|
hls.on(Hls.Events.MANIFEST_PARSED, function(){
|
|
1011
1018
|
if(hlsResumeWarmup && seekTime>0){
|
|
1019
|
+
var loadAt=hlsPriorLoadTime(seekTime);
|
|
1012
1020
|
try{
|
|
1013
|
-
hls.startLoad(
|
|
1021
|
+
hls.startLoad(loadAt);
|
|
1014
1022
|
}catch(event){}
|
|
1023
|
+
// 超时兜底:仍未定位则对齐分片起点起播,避免一直转圈
|
|
1024
|
+
setTimeout(function(){
|
|
1025
|
+
if(!hlsResumeWarmup || !seekTime){return;}
|
|
1026
|
+
var target=seekTime;
|
|
1027
|
+
if(hlsCanSafelySeekTo(target)){
|
|
1028
|
+
finishResumeSeek(target);
|
|
1029
|
+
return;
|
|
1030
|
+
}
|
|
1031
|
+
var snap=hlsPriorLoadTime(target);
|
|
1032
|
+
hlsResumeWarmup=false;
|
|
1033
|
+
finishResumeSeek(snap);
|
|
1034
|
+
},5000);
|
|
1015
1035
|
return;
|
|
1016
1036
|
}
|
|
1017
1037
|
if(!hlsStartPositionUsed){
|
|
@@ -1020,10 +1040,8 @@
|
|
|
1020
1040
|
});
|
|
1021
1041
|
hls.on(Hls.Events.FRAG_BUFFERED, function(event, data){
|
|
1022
1042
|
if(hlsResumeWarmup && seekTime>0){
|
|
1023
|
-
// 必须等「目标 + 前序分片」都在同一连续缓冲里,再定位
|
|
1024
1043
|
if(hlsCanSafelySeekTo(seekTime)){
|
|
1025
|
-
|
|
1026
|
-
applySeekToMedia(seekTime);
|
|
1044
|
+
finishResumeSeek(seekTime);
|
|
1027
1045
|
}
|
|
1028
1046
|
return;
|
|
1029
1047
|
}
|
|
@@ -1033,7 +1051,6 @@
|
|
|
1033
1051
|
var fragStart=data.frag.start;
|
|
1034
1052
|
var fragEnd=fragStart+data.frag.duration;
|
|
1035
1053
|
if(hlsResumeTarget>=fragStart-0.5 && hlsResumeTarget<=fragEnd+0.5){
|
|
1036
|
-
// 续播分片已进入缓冲,解除 waiting
|
|
1037
1054
|
if(!waited){
|
|
1038
1055
|
waited=true;
|
|
1039
1056
|
if(C['buffer']){
|
|
@@ -1833,38 +1850,114 @@
|
|
|
1833
1850
|
}catch(event){}
|
|
1834
1851
|
return false;
|
|
1835
1852
|
},
|
|
1853
|
+
/*
|
|
1854
|
+
* hlsFragAround
|
|
1855
|
+
* 功能:查找覆盖指定时间的 HLS 分片(用于对齐关键帧起点)
|
|
1856
|
+
*/
|
|
1857
|
+
hlsFragAround=function(t){
|
|
1858
|
+
if(!hls || isNaN(t) || t<0){
|
|
1859
|
+
return null;
|
|
1860
|
+
}
|
|
1861
|
+
try{
|
|
1862
|
+
var level=-1;
|
|
1863
|
+
if(typeof hls.currentLevel==='number' && hls.currentLevel>=0){
|
|
1864
|
+
level=hls.currentLevel;
|
|
1865
|
+
}
|
|
1866
|
+
else if(typeof hls.firstLevel==='number' && hls.firstLevel>=0){
|
|
1867
|
+
level=hls.firstLevel;
|
|
1868
|
+
}
|
|
1869
|
+
else{
|
|
1870
|
+
level=0;
|
|
1871
|
+
}
|
|
1872
|
+
var details=hls.levels && hls.levels[level] && hls.levels[level].details;
|
|
1873
|
+
var frags=details && details.fragments;
|
|
1874
|
+
if(!frags || !frags.length){
|
|
1875
|
+
return null;
|
|
1876
|
+
}
|
|
1877
|
+
for(var i=0;i<frags.length;i++){
|
|
1878
|
+
var f=frags[i];
|
|
1879
|
+
if(!f){continue;}
|
|
1880
|
+
var start=f.start||0;
|
|
1881
|
+
var end=start+(f.duration||0);
|
|
1882
|
+
if(t>=start-0.05 && t<end+0.05){
|
|
1883
|
+
return f;
|
|
1884
|
+
}
|
|
1885
|
+
}
|
|
1886
|
+
// 超出末片时回退最后一片
|
|
1887
|
+
return frags[frags.length-1];
|
|
1888
|
+
}catch(event){}
|
|
1889
|
+
return null;
|
|
1890
|
+
},
|
|
1836
1891
|
/*
|
|
1837
1892
|
* hlsPriorLoadTime
|
|
1838
|
-
*
|
|
1893
|
+
* 功能:续播加载起点——优先对齐目标分片起点(该流分片开头通常有 IDR)
|
|
1839
1894
|
*/
|
|
1840
1895
|
hlsPriorLoadTime=function(t){
|
|
1841
1896
|
if(isNaN(t) || t<=0){
|
|
1842
1897
|
return 0;
|
|
1843
1898
|
}
|
|
1899
|
+
var frag=hlsFragAround(t);
|
|
1900
|
+
if(frag && !isUndefined(frag.start)){
|
|
1901
|
+
return Math.max(0,frag.start);
|
|
1902
|
+
}
|
|
1844
1903
|
return Math.max(0,t-12);
|
|
1845
1904
|
},
|
|
1846
1905
|
/*
|
|
1847
1906
|
* hlsCanSafelySeekTo
|
|
1848
|
-
*
|
|
1849
|
-
*
|
|
1907
|
+
* 功能:目标分片已从分片起点连续缓冲到目标点,才允许 currentTime
|
|
1908
|
+
* 避免分片未完整进入 MSE 时中途 seek 触发 PIPELINE_ERROR_DECODE / 永久 loading
|
|
1850
1909
|
*/
|
|
1851
1910
|
hlsCanSafelySeekTo=function(t){
|
|
1852
1911
|
if(!video || isUndefined(video.buffered) || isNaN(t) || t<0){
|
|
1853
1912
|
return false;
|
|
1854
1913
|
}
|
|
1914
|
+
if(!timeInBuffered(t)){
|
|
1915
|
+
return false;
|
|
1916
|
+
}
|
|
1917
|
+
var fragStart=hlsPriorLoadTime(t);
|
|
1918
|
+
// 同一连续区间需覆盖分片起点→目标
|
|
1855
1919
|
try{
|
|
1856
1920
|
var buf=video.buffered;
|
|
1857
|
-
var needFrom=hlsPriorLoadTime(t);
|
|
1858
1921
|
for(var i=0;i<buf.length;i++){
|
|
1859
1922
|
var start=buf.start(i);
|
|
1860
1923
|
var end=buf.end(i);
|
|
1861
|
-
if(t<=end+0.15
|
|
1924
|
+
if(fragStart>=start-0.35 && t<=end+0.15){
|
|
1862
1925
|
return true;
|
|
1863
1926
|
}
|
|
1864
1927
|
}
|
|
1865
1928
|
}catch(event){}
|
|
1866
1929
|
return false;
|
|
1867
1930
|
},
|
|
1931
|
+
/*
|
|
1932
|
+
* finishResumeSeek
|
|
1933
|
+
* 功能:完成续播定位。只 seek、不自动 play(由用户点击中央按钮/播放键)
|
|
1934
|
+
* 避免 seek 后立刻 play 打断 HLS 分片请求(Network 出现 canceled .ts)并导致永久转圈
|
|
1935
|
+
*/
|
|
1936
|
+
finishResumeSeek=function(t){
|
|
1937
|
+
hlsResumeWarmup=false;
|
|
1938
|
+
// 续播场景取消挂起的自动播放,统一改为点击播放
|
|
1939
|
+
autoplayPending=false;
|
|
1940
|
+
applySeekToMedia(t);
|
|
1941
|
+
setTimeout(function(){
|
|
1942
|
+
if(!video){return;}
|
|
1943
|
+
try{
|
|
1944
|
+
video.pause();
|
|
1945
|
+
}catch(event){}
|
|
1946
|
+
paused=true;
|
|
1947
|
+
if(C['buffer']){
|
|
1948
|
+
C['buffer'].hide();
|
|
1949
|
+
}
|
|
1950
|
+
waited=true;
|
|
1951
|
+
if(!vars['controls'] && C['centerPlay']){
|
|
1952
|
+
C['centerPlay'].show();
|
|
1953
|
+
}
|
|
1954
|
+
if(C['bar'] && C['bar']['playAndPause']){
|
|
1955
|
+
C['bar']['playAndPause']['pause'].hide();
|
|
1956
|
+
C['bar']['playAndPause']['play'].show();
|
|
1957
|
+
C['bar']['playAndPause']['refresh'].hide();
|
|
1958
|
+
}
|
|
1959
|
+
},120);
|
|
1960
|
+
},
|
|
1868
1961
|
/*
|
|
1869
1962
|
* clearSeekRetry
|
|
1870
1963
|
* 功能:清理续播等待计时器
|
|
@@ -1933,17 +2026,14 @@
|
|
|
1933
2026
|
firstSeekTime=t;
|
|
1934
2027
|
}
|
|
1935
2028
|
}
|
|
1936
|
-
// HLS 首分片尚未完成预热时不能直接 startLoad(target)
|
|
1937
|
-
// 非关键帧分片会让浏览器解码器永久报错。
|
|
2029
|
+
// HLS 首分片尚未完成预热时不能直接 startLoad(target)
|
|
1938
2030
|
if(hlsResumeWarmup){
|
|
1939
2031
|
if(hlsCanSafelySeekTo(t)){
|
|
1940
|
-
|
|
1941
|
-
applySeekToMedia(t);
|
|
2032
|
+
finishResumeSeek(t);
|
|
1942
2033
|
}
|
|
1943
2034
|
return;
|
|
1944
2035
|
}
|
|
1945
|
-
// HLS:用 hls.startLoad
|
|
1946
|
-
// 若目标附近尚无前序缓冲,改为预热模式从更早处分片加载
|
|
2036
|
+
// HLS:用 hls.startLoad 从目标分片起点加载(去重,避免取消进行中的 .ts)
|
|
1947
2037
|
if(hls && typeof hls.startLoad==='function'){
|
|
1948
2038
|
if(!hlsCanSafelySeekTo(t) && t>1){
|
|
1949
2039
|
hlsResumeWarmup=true;
|
|
@@ -1998,8 +2088,11 @@
|
|
|
1998
2088
|
if(isNaN(resumeAt) || resumeAt<0){
|
|
1999
2089
|
resumeAt=0;
|
|
2000
2090
|
}
|
|
2001
|
-
//
|
|
2002
|
-
var loadFrom=
|
|
2091
|
+
// 优先对齐分片起点(有 IDR);多次失败再往前一片
|
|
2092
|
+
var loadFrom=hlsPriorLoadTime(resumeAt);
|
|
2093
|
+
if(decodeRecoverCount>=2){
|
|
2094
|
+
loadFrom=Math.max(0,loadFrom-10);
|
|
2095
|
+
}
|
|
2003
2096
|
seekApplyLock=false;
|
|
2004
2097
|
clearSeekRetry();
|
|
2005
2098
|
try{
|
|
@@ -2008,7 +2101,9 @@
|
|
|
2008
2101
|
}
|
|
2009
2102
|
}catch(event){}
|
|
2010
2103
|
if(resumeAt>0 && hls && typeof hls.startLoad==='function'){
|
|
2011
|
-
|
|
2104
|
+
// 第 3 次直接落到分片起点起播,保证能出画面
|
|
2105
|
+
var target=(decodeRecoverCount>=3)?loadFrom:resumeAt;
|
|
2106
|
+
seekTime=target;
|
|
2012
2107
|
hlsResumeTarget=resumeAt;
|
|
2013
2108
|
hlsResumeWarmup=true;
|
|
2014
2109
|
hlsLoadAt=-1;
|
|
@@ -2017,31 +2112,20 @@
|
|
|
2017
2112
|
hls.startLoad(loadFrom);
|
|
2018
2113
|
}catch(event){}
|
|
2019
2114
|
setTimeout(function(){
|
|
2020
|
-
if(hlsCanSafelySeekTo(
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
}
|
|
2115
|
+
if(hlsCanSafelySeekTo(target)){
|
|
2116
|
+
finishResumeSeek(target);
|
|
2117
|
+
}
|
|
2118
|
+
else if(decodeRecoverCount>=2){
|
|
2119
|
+
finishResumeSeek(loadFrom);
|
|
2026
2120
|
}
|
|
2027
2121
|
else{
|
|
2028
2122
|
applyPendingSeek(false);
|
|
2029
2123
|
}
|
|
2030
|
-
},
|
|
2124
|
+
},600);
|
|
2031
2125
|
return true;
|
|
2032
2126
|
}
|
|
2033
2127
|
try{
|
|
2034
|
-
|
|
2035
|
-
seekTime=resumeAt;
|
|
2036
|
-
if(video){
|
|
2037
|
-
video.currentTime=soft;
|
|
2038
|
-
}
|
|
2039
|
-
setTimeout(function(){
|
|
2040
|
-
applyPendingSeek(true);
|
|
2041
|
-
if(video && video.paused && vars['autoplay']){
|
|
2042
|
-
try{video.play();}catch(e2){}
|
|
2043
|
-
}
|
|
2044
|
-
},400);
|
|
2128
|
+
finishResumeSeek(Math.max(0,resumeAt-1.5));
|
|
2045
2129
|
return true;
|
|
2046
2130
|
}catch(event){}
|
|
2047
2131
|
return false;
|
|
@@ -2303,7 +2387,8 @@
|
|
|
2303
2387
|
eventTarget('seeked');
|
|
2304
2388
|
},
|
|
2305
2389
|
seeking:function(){
|
|
2306
|
-
|
|
2390
|
+
// 续播/初始化定位中禁止抢先 play:会取消进行中的 .ts 并导致永久 loading
|
|
2391
|
+
if(paused && !seekApplyLock && !hlsResumeWarmup && !seekTime && !autoplayPending){
|
|
2307
2392
|
player.play();
|
|
2308
2393
|
}
|
|
2309
2394
|
var seekingTime=this.currentTime;
|
|
@@ -4763,7 +4848,10 @@
|
|
|
4763
4848
|
}
|
|
4764
4849
|
}
|
|
4765
4850
|
seekTime=player.time();
|
|
4766
|
-
|
|
4851
|
+
// 切换清晰度只保留进度,不强制 autoplay(避免分片 canceled + 未经点击就播放)
|
|
4852
|
+
try{
|
|
4853
|
+
video.removeAttr('autoplay');
|
|
4854
|
+
}catch(event){}
|
|
4767
4855
|
changeVideo(ele.attr('data-video'));
|
|
4768
4856
|
message(language['switchTo']+ele.htm());
|
|
4769
4857
|
C['bar']['definition']['button'].htm(ele.htm());
|
|
@@ -7194,7 +7282,7 @@
|
|
|
7194
7282
|
if(!isRetry && (!vars || vars['cdnFallback']!==false)){
|
|
7195
7283
|
var cdn=(typeof window!=='undefined' && window.ckplayerPlusCdn) ? window.ckplayerPlusCdn : ckplayerPlusDefaultCdn;
|
|
7196
7284
|
var m=String(file).match(/\/(hls\.js|flv\.js|mpegts\.js|language)\/([^/?#]+)(?:[?#].*)?$/i);
|
|
7197
|
-
if(cdn && cdn.indexOf('1.1.
|
|
7285
|
+
if(cdn && cdn.indexOf('1.1.3')===-1 && m){
|
|
7198
7286
|
if(cdn.slice(-1)!=='/'){
|
|
7199
7287
|
cdn+='/';
|
|
7200
7288
|
}
|