easyproctor-hml 2.7.9 → 2.7.10
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/esm/index.js +25 -5
- package/index.js +25 -5
- package/package.json +1 -1
- package/unpkg/easyproctor.min.js +1 -1
package/esm/index.js
CHANGED
|
@@ -13729,7 +13729,7 @@ var BackgroundUploadService = class _BackgroundUploadService {
|
|
|
13729
13729
|
* @param isFinal Se true, não alinha a 256KB e fecha a sessão com /TOTAL no header.
|
|
13730
13730
|
*/
|
|
13731
13731
|
async processQueue(isFinal = false) {
|
|
13732
|
-
var _a2, _b, _c2, _d, _e3, _f;
|
|
13732
|
+
var _a2, _b, _c2, _d, _e3, _f, _g, _h, _i3;
|
|
13733
13733
|
console.log(`[BackgroundUpload] processQueue init`);
|
|
13734
13734
|
if (this.isProcessing) return;
|
|
13735
13735
|
this.isProcessing = true;
|
|
@@ -13751,6 +13751,26 @@ var BackgroundUploadService = class _BackgroundUploadService {
|
|
|
13751
13751
|
this.isProcessing = false;
|
|
13752
13752
|
return;
|
|
13753
13753
|
}
|
|
13754
|
+
if (pendingChunks.length === 0 && isFinal) {
|
|
13755
|
+
const mimeTypeFallback = (_b = (_a2 = allChunks[allChunks.length - 1]) == null ? void 0 : _a2.mimeType) != null ? _b : "video/webm";
|
|
13756
|
+
try {
|
|
13757
|
+
if (!this.sessionUrl) {
|
|
13758
|
+
this.clearSessionState();
|
|
13759
|
+
return;
|
|
13760
|
+
}
|
|
13761
|
+
const totalBytes = this.currentOffset;
|
|
13762
|
+
await this.uploadData(new ArrayBuffer(0), mimeTypeFallback, 0, totalBytes);
|
|
13763
|
+
await this.chunkStorage.clearUploadedChunks(this.proctoringId);
|
|
13764
|
+
this.clearSessionState();
|
|
13765
|
+
} catch (error) {
|
|
13766
|
+
console.error(
|
|
13767
|
+
"[BackgroundUpload] Falha ao finalizar upload (flush sem pendentes):",
|
|
13768
|
+
error
|
|
13769
|
+
);
|
|
13770
|
+
(_c2 = this.onUploadError) == null ? void 0 : _c2.call(this, 0, error);
|
|
13771
|
+
}
|
|
13772
|
+
return;
|
|
13773
|
+
}
|
|
13754
13774
|
console.log(`[BackgroundUpload] ${pendingChunks.length} chunks pendentes encontrados. Modo final: ${isFinal}`);
|
|
13755
13775
|
let virtualStart = this.totalBytesPurged;
|
|
13756
13776
|
const chunksWithMeta = allChunks.map((c3) => {
|
|
@@ -13762,7 +13782,7 @@ var BackgroundUploadService = class _BackgroundUploadService {
|
|
|
13762
13782
|
const combinedBufferParts = [];
|
|
13763
13783
|
let lastProcessedChunkId = null;
|
|
13764
13784
|
let finalChunkIndex = 0;
|
|
13765
|
-
const mimeType = (
|
|
13785
|
+
const mimeType = (_g = (_f = (_d = pendingChunks[0]) == null ? void 0 : _d.mimeType) != null ? _f : (_e3 = allChunks[allChunks.length - 1]) == null ? void 0 : _e3.mimeType) != null ? _g : "video/webm";
|
|
13766
13786
|
console.log(`[BackgroundUpload] passo 1 ok`);
|
|
13767
13787
|
for (const meta of chunksWithMeta) {
|
|
13768
13788
|
if (this.currentOffset > meta.end) continue;
|
|
@@ -13789,7 +13809,7 @@ var BackgroundUploadService = class _BackgroundUploadService {
|
|
|
13789
13809
|
return;
|
|
13790
13810
|
}
|
|
13791
13811
|
} else {
|
|
13792
|
-
totalSizeForHeader = virtualStart;
|
|
13812
|
+
totalSizeForHeader = fullBuffer.byteLength > 0 ? virtualStart : Math.max(virtualStart, this.currentOffset);
|
|
13793
13813
|
}
|
|
13794
13814
|
console.log(`[BackgroundUpload] passo 4 ok`);
|
|
13795
13815
|
const bufferToSend = sendableSize < fullBuffer.byteLength ? fullBuffer.slice(0, sendableSize) : fullBuffer;
|
|
@@ -13808,7 +13828,7 @@ var BackgroundUploadService = class _BackgroundUploadService {
|
|
|
13808
13828
|
await this.chunkStorage.deleteChunkIds(idsToDelete);
|
|
13809
13829
|
for (const meta of fullySent) {
|
|
13810
13830
|
this.retryCount.delete(meta.chunk.id);
|
|
13811
|
-
(
|
|
13831
|
+
(_h = this.onChunkUploaded) == null ? void 0 : _h.call(this, meta.chunk.id, meta.chunk.chunkIndex);
|
|
13812
13832
|
console.log(
|
|
13813
13833
|
`[BackgroundUpload] Chunk ${meta.chunk.chunkIndex} removido do IndexedDB ap\xF3s upload.`
|
|
13814
13834
|
);
|
|
@@ -13831,7 +13851,7 @@ var BackgroundUploadService = class _BackgroundUploadService {
|
|
|
13831
13851
|
console.log(`[BackgroundUpload] passo 9 ok`);
|
|
13832
13852
|
} catch (error) {
|
|
13833
13853
|
console.error("[BackgroundUpload] Falha no upload:", error);
|
|
13834
|
-
(
|
|
13854
|
+
(_i3 = this.onUploadError) == null ? void 0 : _i3.call(this, lastProcessedChunkId || 0, error);
|
|
13835
13855
|
}
|
|
13836
13856
|
} catch (error) {
|
|
13837
13857
|
console.error("[BackgroundUpload] Erro ao processar fila:", error);
|
package/index.js
CHANGED
|
@@ -31826,7 +31826,7 @@ var BackgroundUploadService = class _BackgroundUploadService {
|
|
|
31826
31826
|
* @param isFinal Se true, não alinha a 256KB e fecha a sessão com /TOTAL no header.
|
|
31827
31827
|
*/
|
|
31828
31828
|
async processQueue(isFinal = false) {
|
|
31829
|
-
var _a2, _b, _c2, _d, _e3, _f;
|
|
31829
|
+
var _a2, _b, _c2, _d, _e3, _f, _g, _h, _i3;
|
|
31830
31830
|
console.log(`[BackgroundUpload] processQueue init`);
|
|
31831
31831
|
if (this.isProcessing) return;
|
|
31832
31832
|
this.isProcessing = true;
|
|
@@ -31848,6 +31848,26 @@ var BackgroundUploadService = class _BackgroundUploadService {
|
|
|
31848
31848
|
this.isProcessing = false;
|
|
31849
31849
|
return;
|
|
31850
31850
|
}
|
|
31851
|
+
if (pendingChunks.length === 0 && isFinal) {
|
|
31852
|
+
const mimeTypeFallback = (_b = (_a2 = allChunks[allChunks.length - 1]) == null ? void 0 : _a2.mimeType) != null ? _b : "video/webm";
|
|
31853
|
+
try {
|
|
31854
|
+
if (!this.sessionUrl) {
|
|
31855
|
+
this.clearSessionState();
|
|
31856
|
+
return;
|
|
31857
|
+
}
|
|
31858
|
+
const totalBytes = this.currentOffset;
|
|
31859
|
+
await this.uploadData(new ArrayBuffer(0), mimeTypeFallback, 0, totalBytes);
|
|
31860
|
+
await this.chunkStorage.clearUploadedChunks(this.proctoringId);
|
|
31861
|
+
this.clearSessionState();
|
|
31862
|
+
} catch (error) {
|
|
31863
|
+
console.error(
|
|
31864
|
+
"[BackgroundUpload] Falha ao finalizar upload (flush sem pendentes):",
|
|
31865
|
+
error
|
|
31866
|
+
);
|
|
31867
|
+
(_c2 = this.onUploadError) == null ? void 0 : _c2.call(this, 0, error);
|
|
31868
|
+
}
|
|
31869
|
+
return;
|
|
31870
|
+
}
|
|
31851
31871
|
console.log(`[BackgroundUpload] ${pendingChunks.length} chunks pendentes encontrados. Modo final: ${isFinal}`);
|
|
31852
31872
|
let virtualStart = this.totalBytesPurged;
|
|
31853
31873
|
const chunksWithMeta = allChunks.map((c3) => {
|
|
@@ -31859,7 +31879,7 @@ var BackgroundUploadService = class _BackgroundUploadService {
|
|
|
31859
31879
|
const combinedBufferParts = [];
|
|
31860
31880
|
let lastProcessedChunkId = null;
|
|
31861
31881
|
let finalChunkIndex = 0;
|
|
31862
|
-
const mimeType = (
|
|
31882
|
+
const mimeType = (_g = (_f = (_d = pendingChunks[0]) == null ? void 0 : _d.mimeType) != null ? _f : (_e3 = allChunks[allChunks.length - 1]) == null ? void 0 : _e3.mimeType) != null ? _g : "video/webm";
|
|
31863
31883
|
console.log(`[BackgroundUpload] passo 1 ok`);
|
|
31864
31884
|
for (const meta of chunksWithMeta) {
|
|
31865
31885
|
if (this.currentOffset > meta.end) continue;
|
|
@@ -31886,7 +31906,7 @@ var BackgroundUploadService = class _BackgroundUploadService {
|
|
|
31886
31906
|
return;
|
|
31887
31907
|
}
|
|
31888
31908
|
} else {
|
|
31889
|
-
totalSizeForHeader = virtualStart;
|
|
31909
|
+
totalSizeForHeader = fullBuffer.byteLength > 0 ? virtualStart : Math.max(virtualStart, this.currentOffset);
|
|
31890
31910
|
}
|
|
31891
31911
|
console.log(`[BackgroundUpload] passo 4 ok`);
|
|
31892
31912
|
const bufferToSend = sendableSize < fullBuffer.byteLength ? fullBuffer.slice(0, sendableSize) : fullBuffer;
|
|
@@ -31905,7 +31925,7 @@ var BackgroundUploadService = class _BackgroundUploadService {
|
|
|
31905
31925
|
await this.chunkStorage.deleteChunkIds(idsToDelete);
|
|
31906
31926
|
for (const meta of fullySent) {
|
|
31907
31927
|
this.retryCount.delete(meta.chunk.id);
|
|
31908
|
-
(
|
|
31928
|
+
(_h = this.onChunkUploaded) == null ? void 0 : _h.call(this, meta.chunk.id, meta.chunk.chunkIndex);
|
|
31909
31929
|
console.log(
|
|
31910
31930
|
`[BackgroundUpload] Chunk ${meta.chunk.chunkIndex} removido do IndexedDB ap\xF3s upload.`
|
|
31911
31931
|
);
|
|
@@ -31928,7 +31948,7 @@ var BackgroundUploadService = class _BackgroundUploadService {
|
|
|
31928
31948
|
console.log(`[BackgroundUpload] passo 9 ok`);
|
|
31929
31949
|
} catch (error) {
|
|
31930
31950
|
console.error("[BackgroundUpload] Falha no upload:", error);
|
|
31931
|
-
(
|
|
31951
|
+
(_i3 = this.onUploadError) == null ? void 0 : _i3.call(this, lastProcessedChunkId || 0, error);
|
|
31932
31952
|
}
|
|
31933
31953
|
} catch (error) {
|
|
31934
31954
|
console.error("[BackgroundUpload] Erro ao processar fila:", error);
|
package/package.json
CHANGED
package/unpkg/easyproctor.min.js
CHANGED
|
@@ -77,7 +77,7 @@ Minimum version required to store current data is: `+o+`.
|
|
|
77
77
|
File size: ${i.size}`),new Error("Failed to upload to AWS")}}async upload(t,r,n){let{file:i,onProgress:o}=t;try{let s=l=>{let h=l.loadedBytes/i.size*100;o&&o(Math.round(h))},a;if(n){if(this.imageBatchNum===this.contImages){let l=[];for(let d=this.imageBatchNum;d<this.imageBatchNum+20;d++)l.push({objectName:`${this.proctoringId}/${this.proctoringId}_${d+1}.jpg`,contentType:"image/jpeg"});(await this.backend.getSignedUrlImage(r,l)).map(d=>{this.imageUrlPackage.push(d)}),this.imageBatchNum+=20}}else a=await this.backend.getSignedUrl(r,i,this.proctoringId);let c=!1;return n?c=await Wt.request({url:this.imageUrlPackage[this.contImages],method:"PUT",headers:{"Content-Type":i.type,"x-ms-blob-type":"BlockBlob"},data:i,onUploadProgress:l=>{s({loadedBytes:l.loaded})}}).then(()=>!0).catch(()=>!1).finally(()=>{this.contImages++}):c=await Wt.request({url:a,method:"PUT",headers:{"Content-Type":i.type,"x-ms-blob-type":"BlockBlob"},data:i,onUploadProgress:l=>{s({loadedBytes:l.loaded})}}).then(()=>!0).catch(()=>!1),{storage:"upload",url:a,uploaded:c}}catch{throw pe.registerError(this.proctoringId,`Failed to upload to AWS
|
|
78
78
|
File name: ${i.name}
|
|
79
79
|
File type: ${i.type}
|
|
80
|
-
File size: ${i.size}`),new Error("Failed to upload to AWS")}}};var Yg="not_shared_first_screen",Zg="not_shared_screen",Ma="multiple_monitors_detected",Qg="proctoring_already_started",Na="proctoring_not_started";var e0="another_stream_active",t0="stream_under_minimum_permitted",r0="browser_not_supported",n0="token_missing",i0="credentials_missing";var o0="spy_scan_api_not_found",Ed="safe_browser_api_not_found";var s0="external_camera_not_started";var za=class extends Oi{constructor(t,r,n="videoPreviewFrameDetection",i="liveViewFrameDetection"){super("ObjectDetector","https://storage.googleapis.com/mediapipe-models/object_detector/efficientdet_lite0/float16/1/efficientdet_lite0.tflite",t,r,n,i)}stopDetection(){super.stopDetection(),this.numPersonsSent>0&&this.handleOk("person_ok","person_detection_on_stream")}displayVideoDetections(t){for(let r of this.children)this.liveView.removeChild(r);this.children.splice(0);for(let r of t.detections){let n=window.innerWidth,i=window.innerHeight,o=this.video.offsetWidth,s=this.video.offsetHeight,a=o/n,c=s/i,l=o-r.boundingBox.width*a-r.boundingBox.originX*a,h=r.boundingBox.originY*c,d=(r.boundingBox.width-10)*a,g=r.boundingBox.height*c,f=document.createElement("p");f.innerText=r.categories[0].categoryName+" - with "+Math.round(parseFloat(r.categories[0].score)*100)+"% confidence.",f.style.right=l-20+"px",f.style.top=h+"px",f.style.width=d+"px";let m={zIndex:"2",position:"absolute",border:"1px dashed #fff"};Object.assign(f.style,{...m,margin:"0",fontSize:"9px",paddingBottom:"5px",paddingTop:"5px",color:"#fff",backgroundColor:"#007f8b"});let u=document.createElement("div");u.setAttribute("class","highlighter"),u.style.right=l-20+"px",u.style.top=h+"px",u.style.width=d+"px",u.style.height=g-20+"px",Object.assign(u.style,{...m,zIndex:"1",background:"rgba(0, 255, 0, 0.25)"}),this.liveView.appendChild(u),this.liveView.appendChild(f),this.children.push(u),this.children.push(f)}}verify(t){let r=t.detections.filter(i=>i.categories.some(o=>o.categoryName==="person")).length,n=t.detections.filter(i=>i.categories.some(o=>o.categoryName==="cell phone")).length;this.paramsConfig.videoBehaviourParameters?.detectPerson&&r!==this.numPersonsSent&&(r===0?(this.handleAlert("no_person_detected","person_detection_on_stream"),this.numPersonsSent=r):r>1?(this.handleAlert("multiple_persons_detected","person_detection_on_stream"),this.numPersonsSent=r):(this.handleOk("person_ok","person_detection_on_stream"),this.numPersonsSent=r)),this.paramsConfig.videoBehaviourParameters?.detectCellPhone&&n!==this.numCellphoneSent&&(n>0?(this.handleAlert("cellphone_detected","mobile_detection_on_stream"),this.numCellphoneSent=n):(this.handleOk("cellphone_ok","mobile_detection_on_stream"),this.numCellphoneSent=n))}};var gn=class{constructor(t){this.volume=null;this.animationFrameId=null;this.stream=t}async start(t={}){return new Promise((r,n)=>{try{this.audioContext=new AudioContext,this.analyser=this.audioContext.createAnalyser(),this.microphone=this.audioContext.createMediaStreamSource(this.stream),this.analyser.smoothingTimeConstant=.8,this.analyser.fftSize=1024,this.microphone.connect(this.analyser);let i=()=>{let o=new Uint8Array(this.analyser.frequencyBinCount);this.analyser.getByteFrequencyData(o);let a=o.reduce((c,l)=>c+l,0)/o.length;this.setVolume(a),t.setVolume&&t.setVolume(a),this.animationFrameId=requestAnimationFrame(i)};this.animationFrameId=requestAnimationFrame(i),r(!0)}catch(i){this.stop(),n(`Error: ${i}`)}})}stop(){this.animationFrameId!==null&&cancelAnimationFrame(this.animationFrameId),this.audioContext?.close(),this.microphone?.disconnect(),this.analyser?.disconnect()}getVolume(){return this.volume}setVolume(t){this.volume=t}};var Hi=class e{constructor(){this.db=null}static{this.DB_NAME="EasyProctorChunksDb"}static{this.DB_VERSION=4}static{this.STORE_NAME="chunks"}static detachedArrayBufferCopy(t){let r=new Uint8Array(t.byteLength);return r.set(new Uint8Array(t)),r.buffer}async connect(){return this.db?this.db:new Promise((t,r)=>{let n=window.indexedDB.open(e.DB_NAME,e.DB_VERSION);n.onerror=()=>{r(new Error("N\xE3o foi poss\xEDvel conectar ao IndexedDB para chunks."))},n.onupgradeneeded=()=>{let i=n.result;i.objectStoreNames.contains(e.STORE_NAME)&&i.deleteObjectStore(e.STORE_NAME);let o=i.createObjectStore(e.STORE_NAME,{keyPath:"id",autoIncrement:!0});o.createIndex("proctoringId","proctoringId",{unique:!1}),o.createIndex("uploaded","uploaded",{unique:!1}),o.createIndex("proctoringId_uploaded",["proctoringId","uploaded"],{unique:!1})},n.onsuccess=()=>{this.db=n.result,t(this.db)}})}async saveChunk(t){let r=await this.connect(),n={proctoringId:t.proctoringId,chunkIndex:t.chunkIndex,arrayBuffer:e.detachedArrayBufferCopy(t.arrayBuffer),timestamp:t.timestamp,uploaded:t.uploaded,mimeType:t.mimeType};return new Promise((i,o)=>{let c=r.transaction(e.STORE_NAME,"readwrite").objectStore(e.STORE_NAME).add(n);c.onsuccess=()=>{i(c.result)},c.onerror=()=>{o(new Error(`Erro ao salvar chunk no IndexedDB: ${c.error?.message}`))}})}async getPendingChunks(t){let r=await this.connect();return new Promise((n,i)=>{let a=r.transaction(e.STORE_NAME,"readonly").objectStore(e.STORE_NAME).index("proctoringId_uploaded"),c=IDBKeyRange.only([t,0]),l=a.getAll(c);l.onsuccess=()=>{let h=l.result.sort((d,g)=>d.chunkIndex-g.chunkIndex);n(h)},l.onerror=()=>{i(new Error(`Erro ao buscar chunks pendentes: ${l.error?.message}`))}})}async getAllChunks(t){let r=await this.connect();return new Promise((n,i)=>{let a=r.transaction(e.STORE_NAME,"readonly").objectStore(e.STORE_NAME).index("proctoringId"),c=IDBKeyRange.only(t),l=a.getAll(c);l.onsuccess=()=>{let h=l.result.sort((d,g)=>d.chunkIndex-g.chunkIndex);n(h)},l.onerror=()=>{i(new Error(`Erro ao buscar todos os chunks: ${l.error?.message}`))}})}async deleteChunkIds(t){if(t.length===0)return;let r=await this.connect();return new Promise((n,i)=>{let o=r.transaction(e.STORE_NAME,"readwrite"),s=o.objectStore(e.STORE_NAME);o.oncomplete=()=>n(),o.onerror=()=>i(new Error(`Erro ao remover chunks em lote: ${o.error?.message??"unknown"}`)),o.onabort=()=>i(new Error(`Remo\xE7\xE3o em lote abortada: ${o.error?.message??"unknown"}`));for(let a of t)s.delete(a)})}async clearUploadedChunks(t){let r=await this.connect();return new Promise((n,i)=>{let s=r.transaction(e.STORE_NAME,"readwrite").objectStore(e.STORE_NAME),a=s.index("proctoringId_uploaded"),c=IDBKeyRange.only([t,1]),l=a.openKeyCursor(c);l.onsuccess=()=>{let h=l.result;h?(s.delete(h.primaryKey),h.continue()):n()},l.onerror=()=>i(new Error(`Erro ao limpar chunks enviados: ${l.error?.message}`))})}async clearAllChunks(t){let r=await this.connect();return new Promise((n,i)=>{let s=r.transaction(e.STORE_NAME,"readwrite").objectStore(e.STORE_NAME),a=s.index("proctoringId"),c=IDBKeyRange.only(t),l=a.openKeyCursor(c);l.onsuccess=()=>{let h=l.result;h?(s.delete(h.primaryKey),h.continue()):n()},l.onerror=()=>i(new Error(`Erro ao limpar todos os chunks: ${l.error?.message}`))})}async hasAnyPendingChunks(){let t=await this.connect();return new Promise((r,n)=>{let s=t.transaction(e.STORE_NAME,"readonly").objectStore(e.STORE_NAME).index("uploaded"),a=IDBKeyRange.only(0),c=s.count(a);c.onsuccess=()=>{r(c.result>0)},c.onerror=()=>n(new Error(`Erro ao verificar chunks pendentes: ${c.error?.message}`))})}async getPendingProctoringIds(){let t=await this.connect();return new Promise((r,n)=>{let s=t.transaction(e.STORE_NAME,"readonly").objectStore(e.STORE_NAME).index("uploaded"),a=IDBKeyRange.only(0),c=s.getAll(a);c.onsuccess=()=>{let l=c.result,h=[...new Set(l.map(d=>d.proctoringId))];r(h)},c.onerror=()=>n(new Error(`Erro ao buscar proctoringIds pendentes: ${c.error?.message}`))})}close(){this.db&&(this.db.close(),this.db=null)}};var C_={pollInterval:5e3,maxRetries:5,baseRetryDelay:2e3,cleanAfterUpload:!0},Wi=class e{constructor(t,r,n,i,o){this.pollTimer=null;this.isProcessing=!1;this.isRunning=!1;this.retryCount=new Map;this.sessionUrl=null;this.currentOffset=0;this.totalBytesPurged=0;this.STORAGE_KEY_PREFIX="ep_upload_session_";this.GCS_CHUNK_SIZE=256*1024;this.proctoringId=t.trim(),this.token=r,this.backend=n,this.chunkStorage=i,this.config={...C_,...o},this.loadSessionState()}loadSessionState(){try{let t=localStorage.getItem(`${this.STORAGE_KEY_PREFIX}${this.proctoringId}`);if(t){let{sessionUrl:r,currentOffset:n,totalBytesPurged:i}=JSON.parse(t);this.sessionUrl=r,this.currentOffset=n,this.totalBytesPurged=i||0}}catch(t){console.warn("[BackgroundUpload] Erro ao carregar estado da sess\xE3o:",t)}}saveSessionState(){try{localStorage.setItem(`${this.STORAGE_KEY_PREFIX}${this.proctoringId}`,JSON.stringify({sessionUrl:this.sessionUrl,currentOffset:this.currentOffset,totalBytesPurged:this.totalBytesPurged}))}catch(t){console.warn("[BackgroundUpload] Erro ao salvar estado da sess\xE3o:",t)}}clearSessionState(){try{localStorage.removeItem(`${this.STORAGE_KEY_PREFIX}${this.proctoringId}`),this.sessionUrl=null,this.currentOffset=0,this.totalBytesPurged=0}catch(t){console.warn("[BackgroundUpload] Erro ao limpar estado da sess\xE3o:",t)}}start(){this.isRunning||(this.isRunning=!0,console.log(`[BackgroundUpload] Iniciando servi\xE7o para proctoringId: ${this.proctoringId}`),this.processQueue(),this.pollTimer=setInterval(()=>{this.processQueue(!1)},this.config.pollInterval))}stop(){this.isRunning=!1,this.pollTimer&&(clearInterval(this.pollTimer),this.pollTimer=null),console.log(`[BackgroundUpload] Servi\xE7o parado para proctoringId: ${this.proctoringId}`)}async flush(){console.log("[BackgroundUpload] Flush: enviando todos os chunks pendentes e finalizando...");let t=0;for(;this.isProcessing&&t<10;)await this.sleep(1e3),t++;let r=0,n=3;for(;r<n;)try{await this.processQueue(!0),console.log("[BackgroundUpload] Flush completado com sucesso.");return}catch(i){r++,console.error(`[BackgroundUpload] Erro no flush (tentativa ${r}/${n}):`,i),r<n&&await this.sleep(2e3)}throw new Error(`[BackgroundUpload] Falha ao finalizar upload ap\xF3s ${n} tentativas.`)}async syncOffset(){if(!this.sessionUrl)return 0;try{console.log("[BackgroundUpload] Sincronizando offset com GCS...");let t=await fetch(this.sessionUrl,{method:"PUT",headers:{"Content-Range":"bytes */*"}});if(console.log(`[BackgroundUpload] Status da sincroniza\xE7\xE3o (syncOffset): ${t.status}`),t.status===308){let r=t.headers.get("Range");if(r){let n=parseInt(r.split("-")[1],10);this.currentOffset=n+1,this.saveSessionState(),console.log(`[BackgroundUpload] Offset sincronizado: ${this.currentOffset}`)}else this.currentOffset=0}else t.ok||t.status===201?(console.log("[BackgroundUpload] Sincroniza\xE7\xE3o indicou upload JA FINALIZADO."),this.currentOffset=-1):console.warn(`[BackgroundUpload] Status inesperado na sincroniza\xE7\xE3o: ${t.status}`)}catch(t){console.warn("[BackgroundUpload] Erro ao sincronizar offset:",t)}return this.currentOffset}async processQueue(t=!1){if(console.log("[BackgroundUpload] processQueue init"),!this.isProcessing){this.isProcessing=!0;try{if(this.sessionUrl&&(await this.syncOffset(),this.currentOffset===-1)){console.log("[BackgroundUpload] Sess\xE3o j\xE1 finalizada no servidor."),this.clearSessionState(),this.isProcessing=!1;return}console.log("[BackgroundUpload] processQueue syncOffset ok");let r=await this.chunkStorage.getAllChunks(this.proctoringId),n=r.filter(m=>m.uploaded===0);if(console.log("[BackgroundUpload] processQueue getAllChunks ok"),n.length===0&&!t){this.isProcessing=!1;return}console.log(`[BackgroundUpload] ${n.length} chunks pendentes encontrados. Modo final: ${t}`);let i=this.totalBytesPurged,o=r.map(m=>{let u=i,b=u+m.arrayBuffer.byteLength-1;return i+=m.arrayBuffer.byteLength,{chunk:m,start:u,end:b}}),s=[],a=null,c=0,l=n[0]?.mimeType??r[r.length-1]?.mimeType??"video/webm";console.log("[BackgroundUpload] passo 1 ok");for(let m of o){if(this.currentOffset>m.end)continue;let u=Math.max(0,this.currentOffset-m.start),b=m.chunk.arrayBuffer.slice(u);s.push(b),a=m.chunk.id,c=m.chunk.chunkIndex}if(console.log("[BackgroundUpload] passo 2 ok"),s.length===0&&!t){this.isProcessing=!1;return}let h=e.concatArrayBuffers(s);console.log("[BackgroundUpload] passo 3 ok");let d=h.byteLength,g;if(t)g=i;else if(d=Math.floor(h.byteLength/this.GCS_CHUNK_SIZE)*this.GCS_CHUNK_SIZE,d===0){console.log("[BackgroundUpload] Dados insuficientes para atingir 256KB. Aguardando novo chunk..."),this.isProcessing=!1;return}console.log("[BackgroundUpload] passo 4 ok");let f=d<h.byteLength?h.slice(0,d):h;try{await this.uploadData(f,l,c,g),console.log("[BackgroundUpload] passo 5 ok");let m=o.filter(v=>v.chunk.uploaded===0&&v.end<this.currentOffset&&v.chunk.id!=null),u=m.reduce((v,k)=>v+k.chunk.arrayBuffer.byteLength,0),b=m.map(v=>v.chunk.id);if(b.length>0){await this.chunkStorage.deleteChunkIds(b);for(let v of m)this.retryCount.delete(v.chunk.id),this.onChunkUploaded?.(v.chunk.id,v.chunk.chunkIndex),console.log(`[BackgroundUpload] Chunk ${v.chunk.chunkIndex} removido do IndexedDB ap\xF3s upload.`)}console.log("[BackgroundUpload] passo 6 ok"),await this.chunkStorage.clearUploadedChunks(this.proctoringId),console.log("[BackgroundUpload] passo 7 ok"),this.config.cleanAfterUpload&&u>0&&(this.totalBytesPurged+=u,this.saveSessionState(),console.log(`[BackgroundUpload] ${u} bytes limpos do armazenamento local. Total purgado: ${this.totalBytesPurged}`)),console.log("[BackgroundUpload] passo 8 ok"),t&&this.clearSessionState(),console.log("[BackgroundUpload] passo 9 ok")}catch(m){console.error("[BackgroundUpload] Falha no upload:",m),this.onUploadError?.(a||0,m)}}catch(r){console.error("[BackgroundUpload] Erro ao processar fila:",r)}finally{this.isProcessing=!1}}}static concatArrayBuffers(t){if(t.length===0)return new ArrayBuffer(0);let r=t.reduce((o,s)=>o+s.byteLength,0),n=new Uint8Array(r),i=0;for(let o of t)n.set(new Uint8Array(o),i),i+=o.byteLength;return n.buffer.byteLength===r?n.buffer:n.buffer.slice(n.byteOffset,n.byteOffset+n.byteLength)}async uploadData(t,r,n,i){let o=`EP_${this.proctoringId}_camera_0.webm`;if(this.sessionUrl)console.log(`[BackgroundUpload] Usando sess\xE3o GCS existente: ${this.sessionUrl}`);else{let g=await this.backend.initiateUpload(this.token,`${this.proctoringId}/${o}`,r),f=await fetch(g,{method:"POST",headers:{"x-goog-resumable":"start","Content-Type":r}});if(!f.ok)throw new Error(`Falha ao iniciar: ${f.status}`);if(this.sessionUrl=f.headers.get("Location"),!this.sessionUrl)throw new Error("Location header ausente");try{let u=new URL(this.sessionUrl).pathname.split("/"),b=u[1],v=decodeURIComponent(u.slice(2).join("/"));if(u.includes("b")&&u.includes("o")){let k=u.indexOf("b")+1,S=u.indexOf("o")+1;b=u[k],v=decodeURIComponent(u.slice(S).join("/"))}console.log(`[BackgroundUpload] Sess\xE3o Iniciada -> Bucket: ${b}, Objeto: ${v}`)}catch{console.log(`[BackgroundUpload] Sess\xE3o Iniciada. URL: ${this.sessionUrl}`)}this.currentOffset=0,this.saveSessionState()}let s=this.currentOffset,a=s+t.byteLength-1,c=i!==void 0?i.toString():"*",l=t.byteLength===0&&i!==void 0?`bytes */${c}`:`bytes ${s}-${a}/${c}`;console.log(`[BackgroundUpload] Enviando ${t.byteLength>0?"dados":"finaliza\xE7\xE3o"}: ${l} (Size: ${t.byteLength})`);let h=await fetch(this.sessionUrl,{method:"PUT",headers:{"Content-Range":l},body:t.byteLength>0?t:null});if(console.log(`[BackgroundUpload] Resposta GCS (uploadData): ${h.status}`),h.status!==200&&h.status!==201&&h.status!==308){let g=await h.text();throw console.error(`[BackgroundUpload] Erro GCS: ${g}`),new Error(`Status HTTP inesperado: ${h.status}`)}let d=h.headers.get("Range");if(d){let g=parseInt(d.split("-")[1],10);this.currentOffset=g+1}else this.currentOffset+=t.byteLength;this.saveSessionState(),pe.registerUploadFile(this.proctoringId,`GCS Stream Upload
|
|
80
|
+
File size: ${i.size}`),new Error("Failed to upload to AWS")}}};var Yg="not_shared_first_screen",Zg="not_shared_screen",Ma="multiple_monitors_detected",Qg="proctoring_already_started",Na="proctoring_not_started";var e0="another_stream_active",t0="stream_under_minimum_permitted",r0="browser_not_supported",n0="token_missing",i0="credentials_missing";var o0="spy_scan_api_not_found",Ed="safe_browser_api_not_found";var s0="external_camera_not_started";var za=class extends Oi{constructor(t,r,n="videoPreviewFrameDetection",i="liveViewFrameDetection"){super("ObjectDetector","https://storage.googleapis.com/mediapipe-models/object_detector/efficientdet_lite0/float16/1/efficientdet_lite0.tflite",t,r,n,i)}stopDetection(){super.stopDetection(),this.numPersonsSent>0&&this.handleOk("person_ok","person_detection_on_stream")}displayVideoDetections(t){for(let r of this.children)this.liveView.removeChild(r);this.children.splice(0);for(let r of t.detections){let n=window.innerWidth,i=window.innerHeight,o=this.video.offsetWidth,s=this.video.offsetHeight,a=o/n,c=s/i,l=o-r.boundingBox.width*a-r.boundingBox.originX*a,h=r.boundingBox.originY*c,d=(r.boundingBox.width-10)*a,g=r.boundingBox.height*c,f=document.createElement("p");f.innerText=r.categories[0].categoryName+" - with "+Math.round(parseFloat(r.categories[0].score)*100)+"% confidence.",f.style.right=l-20+"px",f.style.top=h+"px",f.style.width=d+"px";let m={zIndex:"2",position:"absolute",border:"1px dashed #fff"};Object.assign(f.style,{...m,margin:"0",fontSize:"9px",paddingBottom:"5px",paddingTop:"5px",color:"#fff",backgroundColor:"#007f8b"});let u=document.createElement("div");u.setAttribute("class","highlighter"),u.style.right=l-20+"px",u.style.top=h+"px",u.style.width=d+"px",u.style.height=g-20+"px",Object.assign(u.style,{...m,zIndex:"1",background:"rgba(0, 255, 0, 0.25)"}),this.liveView.appendChild(u),this.liveView.appendChild(f),this.children.push(u),this.children.push(f)}}verify(t){let r=t.detections.filter(i=>i.categories.some(o=>o.categoryName==="person")).length,n=t.detections.filter(i=>i.categories.some(o=>o.categoryName==="cell phone")).length;this.paramsConfig.videoBehaviourParameters?.detectPerson&&r!==this.numPersonsSent&&(r===0?(this.handleAlert("no_person_detected","person_detection_on_stream"),this.numPersonsSent=r):r>1?(this.handleAlert("multiple_persons_detected","person_detection_on_stream"),this.numPersonsSent=r):(this.handleOk("person_ok","person_detection_on_stream"),this.numPersonsSent=r)),this.paramsConfig.videoBehaviourParameters?.detectCellPhone&&n!==this.numCellphoneSent&&(n>0?(this.handleAlert("cellphone_detected","mobile_detection_on_stream"),this.numCellphoneSent=n):(this.handleOk("cellphone_ok","mobile_detection_on_stream"),this.numCellphoneSent=n))}};var gn=class{constructor(t){this.volume=null;this.animationFrameId=null;this.stream=t}async start(t={}){return new Promise((r,n)=>{try{this.audioContext=new AudioContext,this.analyser=this.audioContext.createAnalyser(),this.microphone=this.audioContext.createMediaStreamSource(this.stream),this.analyser.smoothingTimeConstant=.8,this.analyser.fftSize=1024,this.microphone.connect(this.analyser);let i=()=>{let o=new Uint8Array(this.analyser.frequencyBinCount);this.analyser.getByteFrequencyData(o);let a=o.reduce((c,l)=>c+l,0)/o.length;this.setVolume(a),t.setVolume&&t.setVolume(a),this.animationFrameId=requestAnimationFrame(i)};this.animationFrameId=requestAnimationFrame(i),r(!0)}catch(i){this.stop(),n(`Error: ${i}`)}})}stop(){this.animationFrameId!==null&&cancelAnimationFrame(this.animationFrameId),this.audioContext?.close(),this.microphone?.disconnect(),this.analyser?.disconnect()}getVolume(){return this.volume}setVolume(t){this.volume=t}};var Hi=class e{constructor(){this.db=null}static{this.DB_NAME="EasyProctorChunksDb"}static{this.DB_VERSION=4}static{this.STORE_NAME="chunks"}static detachedArrayBufferCopy(t){let r=new Uint8Array(t.byteLength);return r.set(new Uint8Array(t)),r.buffer}async connect(){return this.db?this.db:new Promise((t,r)=>{let n=window.indexedDB.open(e.DB_NAME,e.DB_VERSION);n.onerror=()=>{r(new Error("N\xE3o foi poss\xEDvel conectar ao IndexedDB para chunks."))},n.onupgradeneeded=()=>{let i=n.result;i.objectStoreNames.contains(e.STORE_NAME)&&i.deleteObjectStore(e.STORE_NAME);let o=i.createObjectStore(e.STORE_NAME,{keyPath:"id",autoIncrement:!0});o.createIndex("proctoringId","proctoringId",{unique:!1}),o.createIndex("uploaded","uploaded",{unique:!1}),o.createIndex("proctoringId_uploaded",["proctoringId","uploaded"],{unique:!1})},n.onsuccess=()=>{this.db=n.result,t(this.db)}})}async saveChunk(t){let r=await this.connect(),n={proctoringId:t.proctoringId,chunkIndex:t.chunkIndex,arrayBuffer:e.detachedArrayBufferCopy(t.arrayBuffer),timestamp:t.timestamp,uploaded:t.uploaded,mimeType:t.mimeType};return new Promise((i,o)=>{let c=r.transaction(e.STORE_NAME,"readwrite").objectStore(e.STORE_NAME).add(n);c.onsuccess=()=>{i(c.result)},c.onerror=()=>{o(new Error(`Erro ao salvar chunk no IndexedDB: ${c.error?.message}`))}})}async getPendingChunks(t){let r=await this.connect();return new Promise((n,i)=>{let a=r.transaction(e.STORE_NAME,"readonly").objectStore(e.STORE_NAME).index("proctoringId_uploaded"),c=IDBKeyRange.only([t,0]),l=a.getAll(c);l.onsuccess=()=>{let h=l.result.sort((d,g)=>d.chunkIndex-g.chunkIndex);n(h)},l.onerror=()=>{i(new Error(`Erro ao buscar chunks pendentes: ${l.error?.message}`))}})}async getAllChunks(t){let r=await this.connect();return new Promise((n,i)=>{let a=r.transaction(e.STORE_NAME,"readonly").objectStore(e.STORE_NAME).index("proctoringId"),c=IDBKeyRange.only(t),l=a.getAll(c);l.onsuccess=()=>{let h=l.result.sort((d,g)=>d.chunkIndex-g.chunkIndex);n(h)},l.onerror=()=>{i(new Error(`Erro ao buscar todos os chunks: ${l.error?.message}`))}})}async deleteChunkIds(t){if(t.length===0)return;let r=await this.connect();return new Promise((n,i)=>{let o=r.transaction(e.STORE_NAME,"readwrite"),s=o.objectStore(e.STORE_NAME);o.oncomplete=()=>n(),o.onerror=()=>i(new Error(`Erro ao remover chunks em lote: ${o.error?.message??"unknown"}`)),o.onabort=()=>i(new Error(`Remo\xE7\xE3o em lote abortada: ${o.error?.message??"unknown"}`));for(let a of t)s.delete(a)})}async clearUploadedChunks(t){let r=await this.connect();return new Promise((n,i)=>{let s=r.transaction(e.STORE_NAME,"readwrite").objectStore(e.STORE_NAME),a=s.index("proctoringId_uploaded"),c=IDBKeyRange.only([t,1]),l=a.openKeyCursor(c);l.onsuccess=()=>{let h=l.result;h?(s.delete(h.primaryKey),h.continue()):n()},l.onerror=()=>i(new Error(`Erro ao limpar chunks enviados: ${l.error?.message}`))})}async clearAllChunks(t){let r=await this.connect();return new Promise((n,i)=>{let s=r.transaction(e.STORE_NAME,"readwrite").objectStore(e.STORE_NAME),a=s.index("proctoringId"),c=IDBKeyRange.only(t),l=a.openKeyCursor(c);l.onsuccess=()=>{let h=l.result;h?(s.delete(h.primaryKey),h.continue()):n()},l.onerror=()=>i(new Error(`Erro ao limpar todos os chunks: ${l.error?.message}`))})}async hasAnyPendingChunks(){let t=await this.connect();return new Promise((r,n)=>{let s=t.transaction(e.STORE_NAME,"readonly").objectStore(e.STORE_NAME).index("uploaded"),a=IDBKeyRange.only(0),c=s.count(a);c.onsuccess=()=>{r(c.result>0)},c.onerror=()=>n(new Error(`Erro ao verificar chunks pendentes: ${c.error?.message}`))})}async getPendingProctoringIds(){let t=await this.connect();return new Promise((r,n)=>{let s=t.transaction(e.STORE_NAME,"readonly").objectStore(e.STORE_NAME).index("uploaded"),a=IDBKeyRange.only(0),c=s.getAll(a);c.onsuccess=()=>{let l=c.result,h=[...new Set(l.map(d=>d.proctoringId))];r(h)},c.onerror=()=>n(new Error(`Erro ao buscar proctoringIds pendentes: ${c.error?.message}`))})}close(){this.db&&(this.db.close(),this.db=null)}};var C_={pollInterval:5e3,maxRetries:5,baseRetryDelay:2e3,cleanAfterUpload:!0},Wi=class e{constructor(t,r,n,i,o){this.pollTimer=null;this.isProcessing=!1;this.isRunning=!1;this.retryCount=new Map;this.sessionUrl=null;this.currentOffset=0;this.totalBytesPurged=0;this.STORAGE_KEY_PREFIX="ep_upload_session_";this.GCS_CHUNK_SIZE=256*1024;this.proctoringId=t.trim(),this.token=r,this.backend=n,this.chunkStorage=i,this.config={...C_,...o},this.loadSessionState()}loadSessionState(){try{let t=localStorage.getItem(`${this.STORAGE_KEY_PREFIX}${this.proctoringId}`);if(t){let{sessionUrl:r,currentOffset:n,totalBytesPurged:i}=JSON.parse(t);this.sessionUrl=r,this.currentOffset=n,this.totalBytesPurged=i||0}}catch(t){console.warn("[BackgroundUpload] Erro ao carregar estado da sess\xE3o:",t)}}saveSessionState(){try{localStorage.setItem(`${this.STORAGE_KEY_PREFIX}${this.proctoringId}`,JSON.stringify({sessionUrl:this.sessionUrl,currentOffset:this.currentOffset,totalBytesPurged:this.totalBytesPurged}))}catch(t){console.warn("[BackgroundUpload] Erro ao salvar estado da sess\xE3o:",t)}}clearSessionState(){try{localStorage.removeItem(`${this.STORAGE_KEY_PREFIX}${this.proctoringId}`),this.sessionUrl=null,this.currentOffset=0,this.totalBytesPurged=0}catch(t){console.warn("[BackgroundUpload] Erro ao limpar estado da sess\xE3o:",t)}}start(){this.isRunning||(this.isRunning=!0,console.log(`[BackgroundUpload] Iniciando servi\xE7o para proctoringId: ${this.proctoringId}`),this.processQueue(),this.pollTimer=setInterval(()=>{this.processQueue(!1)},this.config.pollInterval))}stop(){this.isRunning=!1,this.pollTimer&&(clearInterval(this.pollTimer),this.pollTimer=null),console.log(`[BackgroundUpload] Servi\xE7o parado para proctoringId: ${this.proctoringId}`)}async flush(){console.log("[BackgroundUpload] Flush: enviando todos os chunks pendentes e finalizando...");let t=0;for(;this.isProcessing&&t<10;)await this.sleep(1e3),t++;let r=0,n=3;for(;r<n;)try{await this.processQueue(!0),console.log("[BackgroundUpload] Flush completado com sucesso.");return}catch(i){r++,console.error(`[BackgroundUpload] Erro no flush (tentativa ${r}/${n}):`,i),r<n&&await this.sleep(2e3)}throw new Error(`[BackgroundUpload] Falha ao finalizar upload ap\xF3s ${n} tentativas.`)}async syncOffset(){if(!this.sessionUrl)return 0;try{console.log("[BackgroundUpload] Sincronizando offset com GCS...");let t=await fetch(this.sessionUrl,{method:"PUT",headers:{"Content-Range":"bytes */*"}});if(console.log(`[BackgroundUpload] Status da sincroniza\xE7\xE3o (syncOffset): ${t.status}`),t.status===308){let r=t.headers.get("Range");if(r){let n=parseInt(r.split("-")[1],10);this.currentOffset=n+1,this.saveSessionState(),console.log(`[BackgroundUpload] Offset sincronizado: ${this.currentOffset}`)}else this.currentOffset=0}else t.ok||t.status===201?(console.log("[BackgroundUpload] Sincroniza\xE7\xE3o indicou upload JA FINALIZADO."),this.currentOffset=-1):console.warn(`[BackgroundUpload] Status inesperado na sincroniza\xE7\xE3o: ${t.status}`)}catch(t){console.warn("[BackgroundUpload] Erro ao sincronizar offset:",t)}return this.currentOffset}async processQueue(t=!1){if(console.log("[BackgroundUpload] processQueue init"),!this.isProcessing){this.isProcessing=!0;try{if(this.sessionUrl&&(await this.syncOffset(),this.currentOffset===-1)){console.log("[BackgroundUpload] Sess\xE3o j\xE1 finalizada no servidor."),this.clearSessionState(),this.isProcessing=!1;return}console.log("[BackgroundUpload] processQueue syncOffset ok");let r=await this.chunkStorage.getAllChunks(this.proctoringId),n=r.filter(m=>m.uploaded===0);if(console.log("[BackgroundUpload] processQueue getAllChunks ok"),n.length===0&&!t){this.isProcessing=!1;return}if(n.length===0&&t){let m=r[r.length-1]?.mimeType??"video/webm";try{if(!this.sessionUrl){this.clearSessionState();return}let u=this.currentOffset;await this.uploadData(new ArrayBuffer(0),m,0,u),await this.chunkStorage.clearUploadedChunks(this.proctoringId),this.clearSessionState()}catch(u){console.error("[BackgroundUpload] Falha ao finalizar upload (flush sem pendentes):",u),this.onUploadError?.(0,u)}return}console.log(`[BackgroundUpload] ${n.length} chunks pendentes encontrados. Modo final: ${t}`);let i=this.totalBytesPurged,o=r.map(m=>{let u=i,b=u+m.arrayBuffer.byteLength-1;return i+=m.arrayBuffer.byteLength,{chunk:m,start:u,end:b}}),s=[],a=null,c=0,l=n[0]?.mimeType??r[r.length-1]?.mimeType??"video/webm";console.log("[BackgroundUpload] passo 1 ok");for(let m of o){if(this.currentOffset>m.end)continue;let u=Math.max(0,this.currentOffset-m.start),b=m.chunk.arrayBuffer.slice(u);s.push(b),a=m.chunk.id,c=m.chunk.chunkIndex}if(console.log("[BackgroundUpload] passo 2 ok"),s.length===0&&!t){this.isProcessing=!1;return}let h=e.concatArrayBuffers(s);console.log("[BackgroundUpload] passo 3 ok");let d=h.byteLength,g;if(t)g=h.byteLength>0?i:Math.max(i,this.currentOffset);else if(d=Math.floor(h.byteLength/this.GCS_CHUNK_SIZE)*this.GCS_CHUNK_SIZE,d===0){console.log("[BackgroundUpload] Dados insuficientes para atingir 256KB. Aguardando novo chunk..."),this.isProcessing=!1;return}console.log("[BackgroundUpload] passo 4 ok");let f=d<h.byteLength?h.slice(0,d):h;try{await this.uploadData(f,l,c,g),console.log("[BackgroundUpload] passo 5 ok");let m=o.filter(v=>v.chunk.uploaded===0&&v.end<this.currentOffset&&v.chunk.id!=null),u=m.reduce((v,k)=>v+k.chunk.arrayBuffer.byteLength,0),b=m.map(v=>v.chunk.id);if(b.length>0){await this.chunkStorage.deleteChunkIds(b);for(let v of m)this.retryCount.delete(v.chunk.id),this.onChunkUploaded?.(v.chunk.id,v.chunk.chunkIndex),console.log(`[BackgroundUpload] Chunk ${v.chunk.chunkIndex} removido do IndexedDB ap\xF3s upload.`)}console.log("[BackgroundUpload] passo 6 ok"),await this.chunkStorage.clearUploadedChunks(this.proctoringId),console.log("[BackgroundUpload] passo 7 ok"),this.config.cleanAfterUpload&&u>0&&(this.totalBytesPurged+=u,this.saveSessionState(),console.log(`[BackgroundUpload] ${u} bytes limpos do armazenamento local. Total purgado: ${this.totalBytesPurged}`)),console.log("[BackgroundUpload] passo 8 ok"),t&&this.clearSessionState(),console.log("[BackgroundUpload] passo 9 ok")}catch(m){console.error("[BackgroundUpload] Falha no upload:",m),this.onUploadError?.(a||0,m)}}catch(r){console.error("[BackgroundUpload] Erro ao processar fila:",r)}finally{this.isProcessing=!1}}}static concatArrayBuffers(t){if(t.length===0)return new ArrayBuffer(0);let r=t.reduce((o,s)=>o+s.byteLength,0),n=new Uint8Array(r),i=0;for(let o of t)n.set(new Uint8Array(o),i),i+=o.byteLength;return n.buffer.byteLength===r?n.buffer:n.buffer.slice(n.byteOffset,n.byteOffset+n.byteLength)}async uploadData(t,r,n,i){let o=`EP_${this.proctoringId}_camera_0.webm`;if(this.sessionUrl)console.log(`[BackgroundUpload] Usando sess\xE3o GCS existente: ${this.sessionUrl}`);else{let g=await this.backend.initiateUpload(this.token,`${this.proctoringId}/${o}`,r),f=await fetch(g,{method:"POST",headers:{"x-goog-resumable":"start","Content-Type":r}});if(!f.ok)throw new Error(`Falha ao iniciar: ${f.status}`);if(this.sessionUrl=f.headers.get("Location"),!this.sessionUrl)throw new Error("Location header ausente");try{let u=new URL(this.sessionUrl).pathname.split("/"),b=u[1],v=decodeURIComponent(u.slice(2).join("/"));if(u.includes("b")&&u.includes("o")){let k=u.indexOf("b")+1,S=u.indexOf("o")+1;b=u[k],v=decodeURIComponent(u.slice(S).join("/"))}console.log(`[BackgroundUpload] Sess\xE3o Iniciada -> Bucket: ${b}, Objeto: ${v}`)}catch{console.log(`[BackgroundUpload] Sess\xE3o Iniciada. URL: ${this.sessionUrl}`)}this.currentOffset=0,this.saveSessionState()}let s=this.currentOffset,a=s+t.byteLength-1,c=i!==void 0?i.toString():"*",l=t.byteLength===0&&i!==void 0?`bytes */${c}`:`bytes ${s}-${a}/${c}`;console.log(`[BackgroundUpload] Enviando ${t.byteLength>0?"dados":"finaliza\xE7\xE3o"}: ${l} (Size: ${t.byteLength})`);let h=await fetch(this.sessionUrl,{method:"PUT",headers:{"Content-Range":l},body:t.byteLength>0?t:null});if(console.log(`[BackgroundUpload] Resposta GCS (uploadData): ${h.status}`),h.status!==200&&h.status!==201&&h.status!==308){let g=await h.text();throw console.error(`[BackgroundUpload] Erro GCS: ${g}`),new Error(`Status HTTP inesperado: ${h.status}`)}let d=h.headers.get("Range");if(d){let g=parseInt(d.split("-")[1],10);this.currentOffset=g+1}else this.currentOffset+=t.byteLength;this.saveSessionState(),pe.registerUploadFile(this.proctoringId,`GCS Stream Upload
|
|
81
81
|
Size: ${t.byteLength}
|
|
82
82
|
Range: ${s}-${a}
|
|
83
83
|
Last Index: ${n}`,"CameraChunk")}static async recoverPendingUploads(t,r){let n=new Hi,i=[];try{let o=await n.getPendingProctoringIds();if(o.length===0)return console.log("[BackgroundUpload] Nenhum chunk pendente encontrado para recupera\xE7\xE3o."),i;console.log(`[BackgroundUpload] Recupera\xE7\xE3o p\xF3s-crash: ${o.length} sess\xE3o(\xF5es) com chunks pendentes.`);for(let s of o)try{await new e(s,r,t,n,{cleanAfterUpload:!0}).flush(),i.push(s),console.log(`[BackgroundUpload] Chunks da sess\xE3o ${s} recuperados com sucesso.`)}catch(a){console.error(`[BackgroundUpload] Erro ao recuperar chunks da sess\xE3o ${s}:`,a)}}catch(o){console.error("[BackgroundUpload] Erro geral na recupera\xE7\xE3o:",o)}return i}sleep(t){return new Promise(r=>setTimeout(r,t))}};var h0=fl(c0()),l0=xd(),A_=l0.default||l0,Zr=class e{constructor(t,r,n,i,o){this.blobs=[];this.paramsConfig={audioBehaviourParameters:{recordingBitrate:128,noiseLimit:40},imageBehaviourParameters:{useUploadImage:!0,uploadInterval:20,saveVideo:!0},videoBehaviourParameters:{detectPerson:!1,detectFace:!1,detectCellPhone:!1}};this.options={cameraId:void 0,microphoneId:void 0,onBufferSizeError:!1,onBufferSizeErrorCallback:t=>{},proctoringType:"IMAGE",onChangeDevicesCallback:t=>{},onRealtimeAlertsCallback:t=>{}};this.videoOptions={width:640,height:480,minWidth:0,minHeight:0};this.blobsRTC=[];this.imageCount=0;this.filesToUpload=[];this.pendingPackages=[];this.animationFrameId=null;this.isCanvasLoopActive=!1;this.hardwareStream=null;this.internalClonedStream=null;this.videoElement=null;this.duration=0;this.stopped=!1;this.backgroundUpload=null;this.chunkIndex=0;this.pendingChunkSaves=[];this.boundVisibilityHandler=null;this.boundPageHideHandler=null;this.currentRetries=0;this.packageCount=0;this.failedUploads=0;this.noiseWait=20;this.options=t,this.videoOptions=r,this.backend=i,this.backendToken=o,n&&(this.paramsConfig=n)}static{this.CHUNK_TIMESLICE_MS=6e4}static{this.LS_SESSION_KEY="ep_proctoring_session"}get isChunkEnabled(){return!!this.proctoringId&&this.options.proctoringType==="REALTIME"&&!Ua()}setProctoringId(t){this.proctoringId=t,this.proctoringId&&this.backend&&(this.upload=new Yr(this.proctoringId,this.backend)),La(t),this.isChunkEnabled?(this.chunkStorage=new Hi,this.backend&&this.backendToken&&(this.backgroundUpload=new Wi(this.proctoringId,this.backendToken,this.backend,this.chunkStorage,{pollInterval:5e3,maxRetries:5,cleanAfterUpload:!0})),this.persistSessionState("IN_PROGRESS"),console.log(`[CameraRecorder] Chunk recording ATIVO (type: ${this.options.proctoringType}, mobile: ${_r()})`)):console.log(`[CameraRecorder] Chunk recording INATIVO (type: ${this.options.proctoringType}) \u2014 modo cl\xE1ssico.`)}persistSessionState(t){try{let r={proctoringId:this.proctoringId,status:t,timestamp:Date.now()};localStorage.setItem(e.LS_SESSION_KEY,JSON.stringify(r))}catch(r){console.warn("[CameraRecorder] N\xE3o foi poss\xEDvel salvar estado no localStorage:",r)}}clearSessionState(){try{localStorage.removeItem(e.LS_SESSION_KEY)}catch(t){console.warn("[CameraRecorder] N\xE3o foi poss\xEDvel limpar estado do localStorage:",t)}}static checkForActiveSession(){try{let t=localStorage.getItem(e.LS_SESSION_KEY);if(!t)return null;let r=JSON.parse(t);return r.status==="IN_PROGRESS"?r:null}catch{return null}}setupLifecycleListeners(){this.boundVisibilityHandler=()=>this.handleVisibilityChange(),this.boundPageHideHandler=()=>this.handlePageHide(),document.addEventListener("visibilitychange",this.boundVisibilityHandler),window.addEventListener("pagehide",this.boundPageHideHandler)}removeLifecycleListeners(){this.boundVisibilityHandler&&(document.removeEventListener("visibilitychange",this.boundVisibilityHandler),this.boundVisibilityHandler=null),this.boundPageHideHandler&&(window.removeEventListener("pagehide",this.boundPageHideHandler),this.boundPageHideHandler=null)}handleVisibilityChange(){document.visibilityState==="hidden"?(console.log("[CameraRecorder] P\xE1gina ficou invis\xEDvel \u2014 sess\xE3o potencialmente interrompida."),this.persistSessionState("INTERRUPTED"),this.proctoringId&&pe.registerError(this.proctoringId,"Visibility API: P\xE1gina ficou oculta (hidden). Poss\xEDvel troca de app ou minimiza\xE7\xE3o.")):document.visibilityState==="visible"&&(console.log("[CameraRecorder] P\xE1gina vis\xEDvel novamente \u2014 verificando estado da grava\xE7\xE3o."),this.persistSessionState("IN_PROGRESS"),this.proctoringId&&pe.registerError(this.proctoringId,"Visibility API: P\xE1gina voltou a ficar vis\xEDvel. Usu\xE1rio retornou."),this.onVisibilityRestored?.())}handlePageHide(){console.log("[CameraRecorder] pagehide detectado \u2014 persistindo estado."),this.persistSessionState("INTERRUPTED"),this.proctoringId&&pe.registerError(this.proctoringId,"Page Lifecycle: pagehide event detectado. P\xE1gina est\xE1 sendo descarregada.")}async initializeDetectors(){lg(),(this.paramsConfig.videoBehaviourParameters?.detectPerson||this.paramsConfig.videoBehaviourParameters?.detectCellPhone)&&(this.objectDetection=new za({onRealtimeAlertsCallback:t=>{t.begin=Date.now()-(this.getStartTime()?.getTime()||0),t.end=Date.now()-(this.getStartTime()?.getTime()||0),this.options.onRealtimeAlertsCallback(t)}},this.paramsConfig),await this.objectDetection.initializeDetector()),this.paramsConfig.videoBehaviourParameters?.detectFace&&(this.faceDetection=new Ui({onRealtimeAlertsCallback:t=>{t.begin=Date.now()-(this.getStartTime()?.getTime()||0),t.end=Date.now()-(this.getStartTime()?.getTime()||0),this.options.onRealtimeAlertsCallback(t)}},this.paramsConfig),await this.faceDetection.initializeDetector())}configImageCapture(){this.video=document.createElement("video"),this.canvas=document.createElement("canvas"),this.video.srcObject=this.cameraStream,this.video.play(),this.video.muted=!0,screen.orientation?.type.includes("portrait")&&_r()?(console.log("configurando canvas em portrait"),this.canvas.width=this.videoOptions.height/2,this.canvas.height=this.videoOptions.width/2):(this.canvas.width=this.videoOptions.width/2,this.canvas.height=this.videoOptions.height/2)}async bufferError(t){console.log("buffer error Camera Recorder params ");let r=this.paramsConfig.videoBehaviourParameters?.retryEnabled||!1,n=this.paramsConfig.videoBehaviourParameters?.maxRetries||3;r&&this.currentRetries<n?(await this.recordingStop(),await this.startRecording(),this.currentRetries++,this.options.onBufferSizeErrorCallback&&this.options.onBufferSizeErrorCallback(this.cameraStream)):this.options.onBufferSizeErrorCallback&&this.options.onBufferSizeErrorCallback()}async startStream(){let{cameraId:t,microphoneId:r,onBufferSizeErrorCallback:n}=this.options,i={audio:{deviceId:r},video:{deviceId:t,width:this.videoOptions.width,height:this.videoOptions.height,frameRate:15}};try{this.hardwareStream=await navigator.mediaDevices.getUserMedia(i)}catch(h){throw h.toString()=="NotReadableError: Could not start video source"?"N\xE3o foi poss\xEDvel conectar a camera, ela pode estar sendo utilizada por outro programa":h}this.cameraStream=this.hardwareStream;let s=this.cameraStream.getVideoTracks()[0].getSettings(),{width:a=0,height:c=0}=s;if(screen.orientation?.type.includes("portrait")&&_r()&&this.videoOptions.width==c&&this.videoOptions.height==a&&([a,c]=[c,a]),this.videoOptions.minWidth>a||this.videoOptions.minHeight>c)throw t0;if(this.videoOptions.width!==a||this.videoOptions.height!==c)throw pe.registerAnotherStream(this.proctoringId,`Maybe have another stream active
|