ckplayer-plus 1.1.2 → 1.1.4

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/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.2/dist/';
144
+ var ckplayerPlusDefaultCdn='https://cdn.jsdelivr.net/npm/ckplayer-plus@1.1.4/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.2')>-1){
449
+ if(!allowCdn || !cdn || cdn.indexOf('1.1.4')>-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
- autoplayPending=true;
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,12 +769,17 @@
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
769
779
  * 功能:HLS 跳转(去重,避免重复 startLoad 取消进行中的 .ts 请求)
780
+ * 一律按分片对齐点 startLoad;缓冲已覆盖但 currentTime 未到位时只 finishResumeSeek
770
781
  * @force 强制重新加载
771
- * @returns true=已由 HLS 处理(含 no-op),false=非 HLS 或未处理
782
+ * @returns true=已由 HLS 处理(含 no-op / 已交 finishResumeSeek),false=非 HLS 或未处理
772
783
  */
773
784
  hlsSeekTo=function(target,force){
774
785
  if(!hls || typeof hls.startLoad!=='function' || !video){
@@ -778,28 +789,30 @@
778
789
  target=0;
779
790
  }
780
791
  var curTime=video.currentTime || 0;
781
- if(!force){
782
- // startPosition / 上次 startLoad 已针对同一续播点,且缓冲已覆盖 → 不再重启
783
- if(hlsResumeTarget>=0 && Math.abs(hlsResumeTarget-target)<1.5){
784
- if(timeInBuffered(target) && Math.abs(curTime-target)<2.5){
785
- return true;
786
- }
792
+ hlsResumeTarget=target;
793
+ // 缓冲已覆盖:只定位,禁止再 startLoad(否则会 cancel 刚下好的中段分片)
794
+ if(!force && timeInBuffered(target)){
795
+ if(Math.abs(curTime-target)<1.5){
796
+ return true;
787
797
  }
788
- // 已在目标附近且可播 → 跳过
789
- if(timeInBuffered(target) && Math.abs(curTime-target)<1.5){
790
- hlsResumeTarget=target;
798
+ if(hlsCanSafelySeekTo(target)){
799
+ finishResumeSeek(target);
791
800
  return true;
792
801
  }
793
- // 短时间内重复 seek 同一点 跳过(防止 onReady 再 seek 取消分片)
794
- if(hlsLoadAt>=0 && Math.abs(hlsLoadAt-target)<1.5 && (Date.now()-hlsLoadTs)<4000){
802
+ // 目标已在缓冲但缺少前片连续区继续按对齐点预热,勿 noop
803
+ }
804
+ var loadAt=hlsPriorLoadTime(target);
805
+ if(!force){
806
+ // 短时间内已对同一对齐点发起加载 → 跳过(防止 onReady 再 seek 取消分片)
807
+ if(hlsLoadAt>=0 && Math.abs(hlsLoadAt-loadAt)<1.5 && (Date.now()-hlsLoadTs)<4000){
795
808
  return true;
796
809
  }
797
810
  }
798
- hlsLoadAt=target;
811
+ hlsLoadAt=loadAt;
799
812
  hlsLoadTs=Date.now();
800
- hlsResumeTarget=target;
813
+ hlsResumeWarmup=true;
801
814
  try{
802
- hls.startLoad(target);
815
+ hls.startLoad(loadAt);
803
816
  }catch(event){
804
817
  return false;
805
818
  }
@@ -990,8 +1003,8 @@
990
1003
  if(!resumeAt){
991
1004
  resumeAt=parseResumeSeekFromVars();
992
1005
  }
993
- // HLS 续播:不要用精确秒数做 startPosition(易在分片中部起播导致解码失败/永久 loading)。
994
- // 对齐目标分片起点加载,等分片从起点缓冲到目标后再一次 currentTime,并强制 play
1006
+ // HLS 续播:autoStartLoad=false,避免默认从 0 拉片后再 startLoad 导致 .ts canceled。
1007
+ // 清单解析后只 startLoad 一次(对齐目标前一片),缓冲覆盖后再 currentTime。
995
1008
  if(resumeAt && resumeAt>0 && (isUndefined(vars['live']) || !vars['live'])){
996
1009
  maxSeeTime=Math.max(maxSeeTime,resumeAt);
997
1010
  if(firstSeekTime<0){
@@ -1000,6 +1013,7 @@
1000
1013
  seekTime=resumeAt;
1001
1014
  hlsResumeTarget=resumeAt;
1002
1015
  hlsResumeWarmup=true;
1016
+ hlsConf.autoStartLoad=false;
1003
1017
  }
1004
1018
  hls = new Hls(hlsConf);
1005
1019
  hls.loadSource(url);
@@ -1007,10 +1021,13 @@
1007
1021
  hls.on(Hls.Events.MANIFEST_PARSED, function(){
1008
1022
  if(hlsResumeWarmup && seekTime>0){
1009
1023
  var loadAt=hlsPriorLoadTime(seekTime);
1024
+ hlsLoadAt=loadAt;
1025
+ hlsLoadTs=Date.now();
1026
+ hlsStartPositionUsed=true;
1010
1027
  try{
1011
1028
  hls.startLoad(loadAt);
1012
1029
  }catch(event){}
1013
- // 超时兜底:仍未定位则对齐分片起点起播,避免一直转圈
1030
+ // 超时兜底:仍未定位则落到可解码的对齐点,避免一直转圈
1014
1031
  setTimeout(function(){
1015
1032
  if(!hlsResumeWarmup || !seekTime){return;}
1016
1033
  var target=seekTime;
@@ -1018,9 +1035,12 @@
1018
1035
  finishResumeSeek(target);
1019
1036
  return;
1020
1037
  }
1021
- var snap=hlsPriorLoadTime(target);
1022
- hlsResumeWarmup=false;
1023
- finishResumeSeek(snap);
1038
+ var snap=hlsFragStartTime(target);
1039
+ if(timeInBuffered(snap) || hlsCanSafelySeekTo(snap)){
1040
+ finishResumeSeek(snap);
1041
+ return;
1042
+ }
1043
+ finishResumeSeek(Math.max(0,loadAt));
1024
1044
  },5000);
1025
1045
  return;
1026
1046
  }
@@ -1841,11 +1861,11 @@
1841
1861
  return false;
1842
1862
  },
1843
1863
  /*
1844
- * hlsFragAround
1845
- * 功能:查找覆盖指定时间的 HLS 分片(用于对齐关键帧起点)
1864
+ * hlsGetFragments
1865
+ * 功能:取当前/首选清晰度的分片列表
1846
1866
  */
1847
- hlsFragAround=function(t){
1848
- if(!hls || isNaN(t) || t<0){
1867
+ hlsGetFragments=function(){
1868
+ if(!hls){
1849
1869
  return null;
1850
1870
  }
1851
1871
  try{
@@ -1864,6 +1884,23 @@
1864
1884
  if(!frags || !frags.length){
1865
1885
  return null;
1866
1886
  }
1887
+ return frags;
1888
+ }catch(event){}
1889
+ return null;
1890
+ },
1891
+ /*
1892
+ * hlsFragAround
1893
+ * 功能:查找覆盖指定时间的 HLS 分片(用于对齐关键帧起点)
1894
+ */
1895
+ hlsFragAround=function(t){
1896
+ if(!hls || isNaN(t) || t<0){
1897
+ return null;
1898
+ }
1899
+ try{
1900
+ var frags=hlsGetFragments();
1901
+ if(!frags){
1902
+ return null;
1903
+ }
1867
1904
  for(var i=0;i<frags.length;i++){
1868
1905
  var f=frags[i];
1869
1906
  if(!f){continue;}
@@ -1878,23 +1915,70 @@
1878
1915
  }catch(event){}
1879
1916
  return null;
1880
1917
  },
1918
+ /*
1919
+ * hlsFragIndexAround
1920
+ * 功能:返回覆盖指定时间的分片下标
1921
+ */
1922
+ hlsFragIndexAround=function(t){
1923
+ var frags=hlsGetFragments();
1924
+ if(!frags || isNaN(t) || t<0){
1925
+ return -1;
1926
+ }
1927
+ for(var i=0;i<frags.length;i++){
1928
+ var f=frags[i];
1929
+ if(!f){continue;}
1930
+ var start=f.start||0;
1931
+ var end=start+(f.duration||0);
1932
+ if(t>=start-0.05 && t<end+0.05){
1933
+ return i;
1934
+ }
1935
+ }
1936
+ return frags.length-1;
1937
+ },
1938
+ /*
1939
+ * hlsFragStartTime
1940
+ * 功能:目标所在分片的起点(用于超时兜底定位)
1941
+ */
1942
+ hlsFragStartTime=function(t){
1943
+ if(isNaN(t) || t<=0){
1944
+ return 0;
1945
+ }
1946
+ var frag=hlsFragAround(t);
1947
+ if(frag && !isUndefined(frag.start)){
1948
+ return Math.max(0,frag.start);
1949
+ }
1950
+ return Math.max(0,t);
1951
+ },
1881
1952
  /*
1882
1953
  * hlsPriorLoadTime
1883
- * 功能:续播加载起点——优先对齐目标分片起点(该流分片开头通常有 IDR
1954
+ * 功能:续播加载起点——回退到目标前一片(教育流片头常无 IDR,需前一片建解码管线)
1884
1955
  */
1885
1956
  hlsPriorLoadTime=function(t){
1886
1957
  if(isNaN(t) || t<=0){
1887
1958
  return 0;
1888
1959
  }
1960
+ var frags=hlsGetFragments();
1961
+ if(frags && frags.length){
1962
+ var idx=hlsFragIndexAround(t);
1963
+ if(idx<0){
1964
+ idx=frags.length-1;
1965
+ }
1966
+ var loadIdx=Math.max(0,idx-1);
1967
+ var loadFrag=frags[loadIdx];
1968
+ if(loadFrag && !isUndefined(loadFrag.start)){
1969
+ return Math.max(0,loadFrag.start);
1970
+ }
1971
+ }
1889
1972
  var frag=hlsFragAround(t);
1890
1973
  if(frag && !isUndefined(frag.start)){
1891
- return Math.max(0,frag.start);
1974
+ // 清单未就绪时至少对齐目标片起点,再额外回退约一片时长
1975
+ return Math.max(0,frag.start-12);
1892
1976
  }
1893
1977
  return Math.max(0,t-12);
1894
1978
  },
1895
1979
  /*
1896
1980
  * hlsCanSafelySeekTo
1897
- * 功能:目标分片已从分片起点连续缓冲到目标点,才允许 currentTime
1981
+ * 功能:从预热加载起点(目标前一片)到目标点已连续缓冲,才允许 currentTime
1898
1982
  * 避免分片未完整进入 MSE 时中途 seek 触发 PIPELINE_ERROR_DECODE / 永久 loading
1899
1983
  */
1900
1984
  hlsCanSafelySeekTo=function(t){
@@ -1904,14 +1988,14 @@
1904
1988
  if(!timeInBuffered(t)){
1905
1989
  return false;
1906
1990
  }
1907
- var fragStart=hlsPriorLoadTime(t);
1908
- // 同一连续区间需覆盖分片起点→目标
1991
+ var loadStart=hlsPriorLoadTime(t);
1992
+ // 同一连续区间需覆盖预热起点→目标
1909
1993
  try{
1910
1994
  var buf=video.buffered;
1911
1995
  for(var i=0;i<buf.length;i++){
1912
1996
  var start=buf.start(i);
1913
1997
  var end=buf.end(i);
1914
- if(fragStart>=start-0.35 && t<=end+0.15){
1998
+ if(loadStart>=start-0.35 && t<=end+0.15){
1915
1999
  return true;
1916
2000
  }
1917
2001
  }
@@ -1920,26 +2004,31 @@
1920
2004
  },
1921
2005
  /*
1922
2006
  * finishResumeSeek
1923
- * 功能:完成续播定位并恢复播放(清除 loading 卡住)
2007
+ * 功能:完成续播定位。只 seek、不自动 play(由用户点击中央按钮/播放键)
2008
+ * 避免 seek 后立刻 play 打断 HLS 分片请求(Network 出现 canceled .ts)并导致永久转圈
1924
2009
  */
1925
2010
  finishResumeSeek=function(t){
1926
2011
  hlsResumeWarmup=false;
2012
+ // 续播场景取消挂起的自动播放,统一改为点击播放
2013
+ autoplayPending=false;
1927
2014
  applySeekToMedia(t);
1928
- // seeked 可能因解码抖动延迟;主动再拉一次播放,避免永久转圈
1929
2015
  setTimeout(function(){
1930
2016
  if(!video){return;}
2017
+ try{
2018
+ video.pause();
2019
+ }catch(event){}
2020
+ paused=true;
1931
2021
  if(C['buffer']){
1932
2022
  C['buffer'].hide();
1933
2023
  }
1934
2024
  waited=true;
1935
- if(vars['autoplay'] || autoplayPending){
1936
- try{
1937
- var p=video.play();
1938
- if(p && typeof p.then==='function'){
1939
- p.catch(function(){});
1940
- }
1941
- }catch(event){}
1942
- autoplayPending=false;
2025
+ if(!vars['controls'] && C['centerPlay']){
2026
+ C['centerPlay'].show();
2027
+ }
2028
+ if(C['bar'] && C['bar']['playAndPause']){
2029
+ C['bar']['playAndPause']['pause'].hide();
2030
+ C['bar']['playAndPause']['play'].show();
2031
+ C['bar']['playAndPause']['refresh'].hide();
1943
2032
  }
1944
2033
  },120);
1945
2034
  },
@@ -1987,6 +2076,7 @@
1987
2076
  * applyPendingSeek
1988
2077
  * 功能:在媒体就绪且目标时间可解码后再应用挂起的 seekTime
1989
2078
  * 避免断点续播过早 seek 触发 PIPELINE_ERROR_DECODE(非关键帧)
2079
+ * seekTime 仅在 finishResumeSeek / applySeekToMedia 成功后清零
1990
2080
  */
1991
2081
  applyPendingSeek=function(force){
1992
2082
  if(!seekTime || !video || seekApplyLock){
@@ -2011,21 +2101,30 @@
2011
2101
  firstSeekTime=t;
2012
2102
  }
2013
2103
  }
2014
- // HLS 首分片尚未完成预热时不能直接 startLoad(target)
2015
- if(hlsResumeWarmup){
2104
+ // HLS:缓冲已可安全定位 只 finish,禁止再 startLoad / 勿提前清 seekTime
2105
+ if(hls && typeof hls.startLoad==='function'){
2016
2106
  if(hlsCanSafelySeekTo(t)){
2017
2107
  finishResumeSeek(t);
2108
+ return;
2018
2109
  }
2019
- return;
2020
- }
2021
- // HLS:用 hls.startLoad 从目标分片起点加载(去重,避免取消进行中的 .ts)
2022
- if(hls && typeof hls.startLoad==='function'){
2023
- if(!hlsCanSafelySeekTo(t) && t>1){
2110
+ // 预热中或需加载:只按对齐点 startLoad(去重),保留 seekTime 等 FRAG_BUFFERED
2111
+ if(t>1){
2024
2112
  hlsResumeWarmup=true;
2025
2113
  hlsResumeTarget=t;
2026
- try{
2027
- hls.startLoad(hlsPriorLoadTime(t));
2028
- }catch(event){}
2114
+ var loadAt=hlsPriorLoadTime(t);
2115
+ var shouldLoad=!!force;
2116
+ if(!shouldLoad){
2117
+ if(!(hlsLoadAt>=0 && Math.abs(hlsLoadAt-loadAt)<1.5 && (Date.now()-hlsLoadTs)<4000)){
2118
+ shouldLoad=true;
2119
+ }
2120
+ }
2121
+ if(shouldLoad){
2122
+ hlsLoadAt=loadAt;
2123
+ hlsLoadTs=Date.now();
2124
+ try{
2125
+ hls.startLoad(loadAt);
2126
+ }catch(event){}
2127
+ }
2029
2128
  seekRetryCount++;
2030
2129
  if(seekRetryCount<=50){
2031
2130
  if(seekRetryTimer){
@@ -2037,11 +2136,6 @@
2037
2136
  }
2038
2137
  return;
2039
2138
  }
2040
- if(hlsSeekTo(t, !!force)){
2041
- clearSeekRetry();
2042
- seekTime=0;
2043
- return;
2044
- }
2045
2139
  }
2046
2140
  // 对普通 MP4,metadata 后设置 currentTime 会由浏览器自动请求目标 Range。
2047
2141
  // 等待目标已缓冲会形成循环等待:目标分片只有 seek 后才会下载。
@@ -2372,8 +2466,8 @@
2372
2466
  eventTarget('seeked');
2373
2467
  },
2374
2468
  seeking:function(){
2375
- // 续播预热/定位过程中不要抢先 play,否则会打断 play Promise 导致永久 loading
2376
- if(paused && !seekApplyLock && !hlsResumeWarmup && !seekTime){
2469
+ // 续播/初始化定位中禁止抢先 play:会取消进行中的 .ts 并导致永久 loading
2470
+ if(paused && !seekApplyLock && !hlsResumeWarmup && !seekTime && !autoplayPending){
2377
2471
  player.play();
2378
2472
  }
2379
2473
  var seekingTime=this.currentTime;
@@ -3434,20 +3528,20 @@
3434
3528
  applyPendingSeek(false);
3435
3529
  return player;
3436
3530
  }
3437
- // HLS:无前序关键帧缓冲时禁止直接 startLoad(target)
3438
- if(hls && target>1 && !hlsCanSafelySeekTo(target)){
3531
+ // HLS:缓冲已覆盖则只定位;否则按对齐点预热,seekTime 保留至 finishResumeSeek
3532
+ if(hls){
3439
3533
  seekTime=target;
3440
- hlsResumeWarmup=true;
3441
3534
  hlsResumeTarget=target;
3442
- try{
3443
- hls.startLoad(hlsPriorLoadTime(target));
3444
- }catch(event){}
3445
- applyPendingSeek(false);
3446
- return player;
3447
- }
3448
- if(hlsSeekTo(target, false)){
3449
- seekTime=0;
3450
- return player;
3535
+ if(target>1 && hlsCanSafelySeekTo(target)){
3536
+ finishResumeSeek(target);
3537
+ return player;
3538
+ }
3539
+ if(target>1){
3540
+ hlsResumeWarmup=true;
3541
+ hlsSeekTo(target, false);
3542
+ applyPendingSeek(false);
3543
+ return player;
3544
+ }
3451
3545
  }
3452
3546
  try{
3453
3547
  video.currentTime=target;
@@ -4833,7 +4927,10 @@
4833
4927
  }
4834
4928
  }
4835
4929
  seekTime=player.time();
4836
- video.attr('autoplay','autoplay');
4930
+ // 切换清晰度只保留进度,不强制 autoplay(避免分片 canceled + 未经点击就播放)
4931
+ try{
4932
+ video.removeAttr('autoplay');
4933
+ }catch(event){}
4837
4934
  changeVideo(ele.attr('data-video'));
4838
4935
  message(language['switchTo']+ele.htm());
4839
4936
  C['bar']['definition']['button'].htm(ele.htm());
@@ -7264,7 +7361,7 @@
7264
7361
  if(!isRetry && (!vars || vars['cdnFallback']!==false)){
7265
7362
  var cdn=(typeof window!=='undefined' && window.ckplayerPlusCdn) ? window.ckplayerPlusCdn : ckplayerPlusDefaultCdn;
7266
7363
  var m=String(file).match(/\/(hls\.js|flv\.js|mpegts\.js|language)\/([^/?#]+)(?:[?#].*)?$/i);
7267
- if(cdn && cdn.indexOf('1.1.2')===-1 && m){
7364
+ if(cdn && cdn.indexOf('1.1.4')===-1 && m){
7268
7365
  if(cdn.slice(-1)!=='/'){
7269
7366
  cdn+='/';
7270
7367
  }