'use strict'; // Pure range logic shared by the UI and regression tests. All ranges are [start,end). const RecordingRanges=(()=>{ // Date truncates fractional milliseconds. Round the query start inward so // MediaMTX never receives a time before this segment's microsecond timestamp. function firstMillisecond(value){ const time=+new Date(value),fraction=typeof value==='string'&&value.match(/\.(\d+)(?:Z|[+-]\d{2}:\d{2})$/); return time+(fraction&&/[1-9]/.test(fraction[1].slice(3))?1:0); } function normalize(items,start,end){ const lo=+start,hi=+end;if(!Number.isFinite(lo)||!Number.isFinite(hi)||hi<=lo)return []; const ranges=(Array.isArray(items)?items:[]).filter(s=>s&&Number.isFinite(+new Date(s.start))&&Number.isFinite(Number(s.duration))&&Number(s.duration)>0).map(s=>({start:Math.max(lo,firstMillisecond(s.start)),end:Math.min(hi,Math.floor(+new Date(s.start)+Number(s.duration)*1000))})) .filter(s=>Number.isFinite(s.start)&&Number.isFinite(s.end)&&s.end>s.start).sort((a,b)=>a.start-b.start); const result=[];for(const r of ranges){const last=result.at(-1);if(last&&r.start<=last.end)last.end=Math.max(last.end,r.end);else result.push({...r});}return result; } function at(ranges,time){const t=+time,s=ranges.find(s=>t>=s.start&&ts.end>+end);return s?at(ranges,Math.max(s.start,+end)):null;} function clip(ranges,start,end){ const a=+start,b=+end; if(!Number.isFinite(a)||!Number.isFinite(b)||b<=a)throw Error('结束时间必须晚于开始时间'); if(b-a>3600000)throw Error('一次下载最多一小时,请缩短片段'); if(!ranges.some(s=>a>=s.start&&b<=s.end))throw Error('所选片段跨越无录像时段或超出查询日期,请选择同一连续录像时段'); return {start:a,end:b}; } return {normalize,at,next,clip}; })(); if(typeof module!=='undefined')module.exports=RecordingRanges;