easyproctor-hml 2.7.9 → 2.7.11
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 +66 -116
- package/index.js +66 -116
- package/new-flow/chunk/BackgroundUploadService.d.ts +0 -1
- package/new-flow/chunk/ChunkStorageService.d.ts +2 -3
- package/package.json +1 -1
- package/unpkg/easyproctor.min.js +33 -33
package/esm/index.js
CHANGED
|
@@ -13317,16 +13317,6 @@ var _ChunkStorageService = class _ChunkStorageService {
|
|
|
13317
13317
|
constructor() {
|
|
13318
13318
|
this.db = null;
|
|
13319
13319
|
}
|
|
13320
|
-
/**
|
|
13321
|
-
* WebKit pode falhar em IDB.add/put com "Error preparing Blob/File data..." se o
|
|
13322
|
-
* ArrayBuffer ainda estiver associado ao Blob (ex.: blob.arrayBuffer()) ou no
|
|
13323
|
-
* round-trip get→put do mesmo registro. Esta cópia remove qualquer vínculo.
|
|
13324
|
-
*/
|
|
13325
|
-
static detachedArrayBufferCopy(src) {
|
|
13326
|
-
const next = new Uint8Array(src.byteLength);
|
|
13327
|
-
next.set(new Uint8Array(src));
|
|
13328
|
-
return next.buffer;
|
|
13329
|
-
}
|
|
13330
13320
|
/**
|
|
13331
13321
|
* Abre a conexão com o IndexedDB, criando o banco e o object store se necessário.
|
|
13332
13322
|
*/
|
|
@@ -13366,18 +13356,10 @@ var _ChunkStorageService = class _ChunkStorageService {
|
|
|
13366
13356
|
*/
|
|
13367
13357
|
async saveChunk(chunk) {
|
|
13368
13358
|
const db = await this.connect();
|
|
13369
|
-
const record = {
|
|
13370
|
-
proctoringId: chunk.proctoringId,
|
|
13371
|
-
chunkIndex: chunk.chunkIndex,
|
|
13372
|
-
arrayBuffer: _ChunkStorageService.detachedArrayBufferCopy(chunk.arrayBuffer),
|
|
13373
|
-
timestamp: chunk.timestamp,
|
|
13374
|
-
uploaded: chunk.uploaded,
|
|
13375
|
-
mimeType: chunk.mimeType
|
|
13376
|
-
};
|
|
13377
13359
|
return new Promise((resolve, reject) => {
|
|
13378
13360
|
const transaction = db.transaction(_ChunkStorageService.STORE_NAME, "readwrite");
|
|
13379
13361
|
const store = transaction.objectStore(_ChunkStorageService.STORE_NAME);
|
|
13380
|
-
const request = store.add(
|
|
13362
|
+
const request = store.add(chunk);
|
|
13381
13363
|
request.onsuccess = () => {
|
|
13382
13364
|
resolve(request.result);
|
|
13383
13365
|
};
|
|
@@ -13434,35 +13416,36 @@ var _ChunkStorageService = class _ChunkStorageService {
|
|
|
13434
13416
|
});
|
|
13435
13417
|
}
|
|
13436
13418
|
/**
|
|
13437
|
-
*
|
|
13419
|
+
* Marca um chunk como enviado (uploaded = 1).
|
|
13438
13420
|
*/
|
|
13439
|
-
async
|
|
13440
|
-
if (chunkIds.length === 0) return;
|
|
13421
|
+
async markAsUploaded(chunkId) {
|
|
13441
13422
|
const db = await this.connect();
|
|
13442
13423
|
return new Promise((resolve, reject) => {
|
|
13443
13424
|
const transaction = db.transaction(_ChunkStorageService.STORE_NAME, "readwrite");
|
|
13444
13425
|
const store = transaction.objectStore(_ChunkStorageService.STORE_NAME);
|
|
13445
|
-
|
|
13446
|
-
|
|
13447
|
-
|
|
13448
|
-
|
|
13449
|
-
|
|
13450
|
-
|
|
13451
|
-
|
|
13452
|
-
|
|
13426
|
+
const getRequest = store.get(chunkId);
|
|
13427
|
+
getRequest.onsuccess = () => {
|
|
13428
|
+
const chunk = getRequest.result;
|
|
13429
|
+
if (!chunk) {
|
|
13430
|
+
resolve();
|
|
13431
|
+
return;
|
|
13432
|
+
}
|
|
13433
|
+
chunk.uploaded = 1;
|
|
13434
|
+
const putRequest = store.put(chunk);
|
|
13435
|
+
putRequest.onsuccess = () => resolve();
|
|
13436
|
+
putRequest.onerror = () => {
|
|
13437
|
+
var _a2;
|
|
13438
|
+
return reject(new Error(`Erro ao marcar chunk como enviado: ${(_a2 = putRequest.error) == null ? void 0 : _a2.message}`));
|
|
13439
|
+
};
|
|
13453
13440
|
};
|
|
13454
|
-
|
|
13455
|
-
var _a2
|
|
13456
|
-
return reject(new Error(`
|
|
13441
|
+
getRequest.onerror = () => {
|
|
13442
|
+
var _a2;
|
|
13443
|
+
return reject(new Error(`Erro ao buscar chunk para marcar: ${(_a2 = getRequest.error) == null ? void 0 : _a2.message}`));
|
|
13457
13444
|
};
|
|
13458
|
-
for (const id of chunkIds) {
|
|
13459
|
-
store.delete(id);
|
|
13460
|
-
}
|
|
13461
13445
|
});
|
|
13462
13446
|
}
|
|
13463
13447
|
/**
|
|
13464
|
-
* Remove
|
|
13465
|
-
* Usa openKeyCursor + delete por primaryKey para não materializar o valor (WebKit/iOS).
|
|
13448
|
+
* Remove todos os chunks já enviados de um proctoringId para liberar espaço.
|
|
13466
13449
|
*/
|
|
13467
13450
|
async clearUploadedChunks(proctoringId2) {
|
|
13468
13451
|
const db = await this.connect();
|
|
@@ -13471,11 +13454,11 @@ var _ChunkStorageService = class _ChunkStorageService {
|
|
|
13471
13454
|
const store = transaction.objectStore(_ChunkStorageService.STORE_NAME);
|
|
13472
13455
|
const index = store.index("proctoringId_uploaded");
|
|
13473
13456
|
const range = IDBKeyRange.only([proctoringId2, 1]);
|
|
13474
|
-
const request = index.
|
|
13457
|
+
const request = index.openCursor(range);
|
|
13475
13458
|
request.onsuccess = () => {
|
|
13476
13459
|
const cursor = request.result;
|
|
13477
13460
|
if (cursor) {
|
|
13478
|
-
|
|
13461
|
+
cursor.delete();
|
|
13479
13462
|
cursor.continue();
|
|
13480
13463
|
} else {
|
|
13481
13464
|
resolve();
|
|
@@ -13497,11 +13480,11 @@ var _ChunkStorageService = class _ChunkStorageService {
|
|
|
13497
13480
|
const store = transaction.objectStore(_ChunkStorageService.STORE_NAME);
|
|
13498
13481
|
const index = store.index("proctoringId");
|
|
13499
13482
|
const range = IDBKeyRange.only(proctoringId2);
|
|
13500
|
-
const request = index.
|
|
13483
|
+
const request = index.openCursor(range);
|
|
13501
13484
|
request.onsuccess = () => {
|
|
13502
13485
|
const cursor = request.result;
|
|
13503
13486
|
if (cursor) {
|
|
13504
|
-
|
|
13487
|
+
cursor.delete();
|
|
13505
13488
|
cursor.continue();
|
|
13506
13489
|
} else {
|
|
13507
13490
|
resolve();
|
|
@@ -13568,8 +13551,8 @@ var _ChunkStorageService = class _ChunkStorageService {
|
|
|
13568
13551
|
}
|
|
13569
13552
|
};
|
|
13570
13553
|
_ChunkStorageService.DB_NAME = "EasyProctorChunksDb";
|
|
13571
|
-
/** v2
|
|
13572
|
-
_ChunkStorageService.DB_VERSION =
|
|
13554
|
+
/** Incrementado para v2 para recriar índices com tipos numéricos em vez de boolean */
|
|
13555
|
+
_ChunkStorageService.DB_VERSION = 2;
|
|
13573
13556
|
_ChunkStorageService.STORE_NAME = "chunks";
|
|
13574
13557
|
var ChunkStorageService = _ChunkStorageService;
|
|
13575
13558
|
|
|
@@ -13729,8 +13712,7 @@ var BackgroundUploadService = class _BackgroundUploadService {
|
|
|
13729
13712
|
* @param isFinal Se true, não alinha a 256KB e fecha a sessão com /TOTAL no header.
|
|
13730
13713
|
*/
|
|
13731
13714
|
async processQueue(isFinal = false) {
|
|
13732
|
-
var _a2, _b
|
|
13733
|
-
console.log(`[BackgroundUpload] processQueue init`);
|
|
13715
|
+
var _a2, _b;
|
|
13734
13716
|
if (this.isProcessing) return;
|
|
13735
13717
|
this.isProcessing = true;
|
|
13736
13718
|
try {
|
|
@@ -13743,10 +13725,8 @@ var BackgroundUploadService = class _BackgroundUploadService {
|
|
|
13743
13725
|
return;
|
|
13744
13726
|
}
|
|
13745
13727
|
}
|
|
13746
|
-
console.log(`[BackgroundUpload] processQueue syncOffset ok`);
|
|
13747
13728
|
const allChunks = await this.chunkStorage.getAllChunks(this.proctoringId);
|
|
13748
13729
|
const pendingChunks = allChunks.filter((c3) => c3.uploaded === 0);
|
|
13749
|
-
console.log(`[BackgroundUpload] processQueue getAllChunks ok`);
|
|
13750
13730
|
if (pendingChunks.length === 0 && !isFinal) {
|
|
13751
13731
|
this.isProcessing = false;
|
|
13752
13732
|
return;
|
|
@@ -13755,34 +13735,31 @@ var BackgroundUploadService = class _BackgroundUploadService {
|
|
|
13755
13735
|
let virtualStart = this.totalBytesPurged;
|
|
13756
13736
|
const chunksWithMeta = allChunks.map((c3) => {
|
|
13757
13737
|
const start = virtualStart;
|
|
13758
|
-
const end = start + c3.
|
|
13759
|
-
virtualStart += c3.
|
|
13738
|
+
const end = start + c3.blob.size - 1;
|
|
13739
|
+
virtualStart += c3.blob.size;
|
|
13760
13740
|
return { chunk: c3, start, end };
|
|
13761
13741
|
});
|
|
13762
|
-
|
|
13742
|
+
let combinedBlobParts = [];
|
|
13763
13743
|
let lastProcessedChunkId = null;
|
|
13764
13744
|
let finalChunkIndex = 0;
|
|
13765
|
-
|
|
13766
|
-
console.log(`[BackgroundUpload] passo 1 ok`);
|
|
13745
|
+
let mimeType = pendingChunks[0].mimeType;
|
|
13767
13746
|
for (const meta of chunksWithMeta) {
|
|
13768
13747
|
if (this.currentOffset > meta.end) continue;
|
|
13769
13748
|
const sliceStart = Math.max(0, this.currentOffset - meta.start);
|
|
13770
|
-
const chunkSlice = meta.chunk.
|
|
13771
|
-
|
|
13749
|
+
const chunkSlice = meta.chunk.blob.slice(sliceStart);
|
|
13750
|
+
combinedBlobParts.push(chunkSlice);
|
|
13772
13751
|
lastProcessedChunkId = meta.chunk.id;
|
|
13773
13752
|
finalChunkIndex = meta.chunk.chunkIndex;
|
|
13774
13753
|
}
|
|
13775
|
-
|
|
13776
|
-
if (combinedBufferParts.length === 0 && !isFinal) {
|
|
13754
|
+
if (combinedBlobParts.length === 0 && !isFinal) {
|
|
13777
13755
|
this.isProcessing = false;
|
|
13778
13756
|
return;
|
|
13779
13757
|
}
|
|
13780
|
-
|
|
13781
|
-
|
|
13782
|
-
let sendableSize = fullBuffer.byteLength;
|
|
13758
|
+
let fullBlob = new Blob(combinedBlobParts, { type: mimeType });
|
|
13759
|
+
let sendableSize = fullBlob.size;
|
|
13783
13760
|
let totalSizeForHeader = void 0;
|
|
13784
13761
|
if (!isFinal) {
|
|
13785
|
-
sendableSize = Math.floor(
|
|
13762
|
+
sendableSize = Math.floor(fullBlob.size / this.GCS_CHUNK_SIZE) * this.GCS_CHUNK_SIZE;
|
|
13786
13763
|
if (sendableSize === 0) {
|
|
13787
13764
|
console.log("[BackgroundUpload] Dados insuficientes para atingir 256KB. Aguardando novo chunk...");
|
|
13788
13765
|
this.isProcessing = false;
|
|
@@ -13791,47 +13768,33 @@ var BackgroundUploadService = class _BackgroundUploadService {
|
|
|
13791
13768
|
} else {
|
|
13792
13769
|
totalSizeForHeader = virtualStart;
|
|
13793
13770
|
}
|
|
13794
|
-
|
|
13795
|
-
const bufferToSend = sendableSize < fullBuffer.byteLength ? fullBuffer.slice(0, sendableSize) : fullBuffer;
|
|
13771
|
+
const blobToSend = fullBlob.slice(0, sendableSize);
|
|
13796
13772
|
try {
|
|
13797
|
-
await this.uploadData(
|
|
13798
|
-
|
|
13799
|
-
|
|
13800
|
-
|
|
13801
|
-
);
|
|
13802
|
-
const sizePurged = fullySent.reduce(
|
|
13803
|
-
(acc, meta) => acc + meta.chunk.arrayBuffer.byteLength,
|
|
13804
|
-
0
|
|
13805
|
-
);
|
|
13806
|
-
const idsToDelete = fullySent.map((m3) => m3.chunk.id);
|
|
13807
|
-
if (idsToDelete.length > 0) {
|
|
13808
|
-
await this.chunkStorage.deleteChunkIds(idsToDelete);
|
|
13809
|
-
for (const meta of fullySent) {
|
|
13773
|
+
await this.uploadData(blobToSend, mimeType, finalChunkIndex, totalSizeForHeader);
|
|
13774
|
+
for (const meta of chunksWithMeta) {
|
|
13775
|
+
if (meta.chunk.uploaded === 0 && meta.end < this.currentOffset) {
|
|
13776
|
+
await this.chunkStorage.markAsUploaded(meta.chunk.id);
|
|
13810
13777
|
this.retryCount.delete(meta.chunk.id);
|
|
13811
|
-
(
|
|
13812
|
-
console.log(
|
|
13813
|
-
`[BackgroundUpload] Chunk ${meta.chunk.chunkIndex} removido do IndexedDB ap\xF3s upload.`
|
|
13814
|
-
);
|
|
13778
|
+
(_a2 = this.onChunkUploaded) == null ? void 0 : _a2.call(this, meta.chunk.id, meta.chunk.chunkIndex);
|
|
13779
|
+
console.log(`[BackgroundUpload] Chunk ${meta.chunk.chunkIndex} marcado como enviado.`);
|
|
13815
13780
|
}
|
|
13816
13781
|
}
|
|
13817
|
-
|
|
13818
|
-
|
|
13819
|
-
|
|
13820
|
-
|
|
13821
|
-
|
|
13822
|
-
|
|
13823
|
-
|
|
13824
|
-
`[BackgroundUpload] ${sizePurged} bytes limpos do armazenamento local. Total purgado: ${this.totalBytesPurged}`
|
|
13825
|
-
|
|
13782
|
+
if (this.config.cleanAfterUpload) {
|
|
13783
|
+
const chunksToClear = chunksWithMeta.filter((meta) => meta.chunk.uploaded === 1 || meta.chunk.uploaded === 0 && meta.end < this.currentOffset);
|
|
13784
|
+
const sizePurged = chunksToClear.reduce((acc, meta) => acc + meta.chunk.blob.size, 0);
|
|
13785
|
+
await this.chunkStorage.clearUploadedChunks(this.proctoringId);
|
|
13786
|
+
if (sizePurged > 0) {
|
|
13787
|
+
this.totalBytesPurged += sizePurged;
|
|
13788
|
+
this.saveSessionState();
|
|
13789
|
+
console.log(`[BackgroundUpload] ${sizePurged} bytes limpos do armazenamento local. Total purgado: ${this.totalBytesPurged}`);
|
|
13790
|
+
}
|
|
13826
13791
|
}
|
|
13827
|
-
console.log(`[BackgroundUpload] passo 8 ok`);
|
|
13828
13792
|
if (isFinal) {
|
|
13829
13793
|
this.clearSessionState();
|
|
13830
13794
|
}
|
|
13831
|
-
console.log(`[BackgroundUpload] passo 9 ok`);
|
|
13832
13795
|
} catch (error) {
|
|
13833
13796
|
console.error("[BackgroundUpload] Falha no upload:", error);
|
|
13834
|
-
(
|
|
13797
|
+
(_b = this.onUploadError) == null ? void 0 : _b.call(this, lastProcessedChunkId || 0, error);
|
|
13835
13798
|
}
|
|
13836
13799
|
} catch (error) {
|
|
13837
13800
|
console.error("[BackgroundUpload] Erro ao processar fila:", error);
|
|
@@ -13842,18 +13805,7 @@ var BackgroundUploadService = class _BackgroundUploadService {
|
|
|
13842
13805
|
/**
|
|
13843
13806
|
* Faz o upload bruto de dados para a sessão GCS.
|
|
13844
13807
|
*/
|
|
13845
|
-
|
|
13846
|
-
if (parts.length === 0) return new ArrayBuffer(0);
|
|
13847
|
-
const total = parts.reduce((sum, p3) => sum + p3.byteLength, 0);
|
|
13848
|
-
const merged = new Uint8Array(total);
|
|
13849
|
-
let offset = 0;
|
|
13850
|
-
for (const p3 of parts) {
|
|
13851
|
-
merged.set(new Uint8Array(p3), offset);
|
|
13852
|
-
offset += p3.byteLength;
|
|
13853
|
-
}
|
|
13854
|
-
return merged.buffer.byteLength === total ? merged.buffer : merged.buffer.slice(merged.byteOffset, merged.byteOffset + merged.byteLength);
|
|
13855
|
-
}
|
|
13856
|
-
async uploadData(data, mimeType, chunkIndex, totalSize) {
|
|
13808
|
+
async uploadData(blob, mimeType, chunkIndex, totalSize) {
|
|
13857
13809
|
const fileName = `EP_${this.proctoringId}_camera_0.webm`;
|
|
13858
13810
|
if (!this.sessionUrl) {
|
|
13859
13811
|
const initiateUrl = await this.backend.initiateUpload(this.token, `${this.proctoringId}/${fileName}`, mimeType);
|
|
@@ -13885,16 +13837,15 @@ var BackgroundUploadService = class _BackgroundUploadService {
|
|
|
13885
13837
|
console.log(`[BackgroundUpload] Usando sess\xE3o GCS existente: ${this.sessionUrl}`);
|
|
13886
13838
|
}
|
|
13887
13839
|
const start = this.currentOffset;
|
|
13888
|
-
const end = start +
|
|
13840
|
+
const end = start + blob.size - 1;
|
|
13889
13841
|
const totalHeader = totalSize !== void 0 ? totalSize.toString() : "*";
|
|
13890
|
-
const contentRangeHeader =
|
|
13891
|
-
console.log(
|
|
13892
|
-
`[BackgroundUpload] Enviando ${data.byteLength > 0 ? "dados" : "finaliza\xE7\xE3o"}: ${contentRangeHeader} (Size: ${data.byteLength})`
|
|
13893
|
-
);
|
|
13842
|
+
const contentRangeHeader = blob.size === 0 && totalSize !== void 0 ? `bytes */${totalHeader}` : `bytes ${start}-${end}/${totalHeader}`;
|
|
13843
|
+
console.log(`[BackgroundUpload] Enviando ${blob.size > 0 ? "dados" : "finaliza\xE7\xE3o"}: ${contentRangeHeader} (Size: ${blob.size})`);
|
|
13894
13844
|
const response = await fetch(this.sessionUrl, {
|
|
13895
13845
|
method: "PUT",
|
|
13896
13846
|
headers: { "Content-Range": contentRangeHeader },
|
|
13897
|
-
body:
|
|
13847
|
+
body: blob.size > 0 ? blob : null
|
|
13848
|
+
// Usa null para garantir corpo vazio se necessário
|
|
13898
13849
|
});
|
|
13899
13850
|
console.log(`[BackgroundUpload] Resposta GCS (uploadData): ${response.status}`);
|
|
13900
13851
|
if (response.status !== 200 && response.status !== 201 && response.status !== 308) {
|
|
@@ -13907,13 +13858,13 @@ var BackgroundUploadService = class _BackgroundUploadService {
|
|
|
13907
13858
|
const lastByte = parseInt(rangeHeader.split("-")[1], 10);
|
|
13908
13859
|
this.currentOffset = lastByte + 1;
|
|
13909
13860
|
} else {
|
|
13910
|
-
this.currentOffset +=
|
|
13861
|
+
this.currentOffset += blob.size;
|
|
13911
13862
|
}
|
|
13912
13863
|
this.saveSessionState();
|
|
13913
13864
|
trackers.registerUploadFile(
|
|
13914
13865
|
this.proctoringId,
|
|
13915
13866
|
`GCS Stream Upload
|
|
13916
|
-
Size: ${
|
|
13867
|
+
Size: ${blob.size}
|
|
13917
13868
|
Range: ${start}-${end}
|
|
13918
13869
|
Last Index: ${chunkIndex}`,
|
|
13919
13870
|
"CameraChunk"
|
|
@@ -14048,7 +13999,7 @@ var _CameraRecorder = class _CameraRecorder {
|
|
|
14048
13999
|
* 2. E (`useChunkRecording` foi explicitamente setado como true OU o dispositivo é mobile)
|
|
14049
14000
|
*/
|
|
14050
14001
|
get isChunkEnabled() {
|
|
14051
|
-
return
|
|
14002
|
+
return false;
|
|
14052
14003
|
}
|
|
14053
14004
|
setProctoringId(proctoringId2) {
|
|
14054
14005
|
this.proctoringId = proctoringId2;
|
|
@@ -14452,15 +14403,14 @@ Setting: ${JSON.stringify(settings, null, 2)}`
|
|
|
14452
14403
|
* Salva o chunk no IndexedDB para persistência e recuperação.
|
|
14453
14404
|
*/
|
|
14454
14405
|
async handleNewChunk(blob, idx) {
|
|
14455
|
-
if (!this.proctoringId || !this.chunkStorage
|
|
14406
|
+
if (!this.proctoringId || !this.chunkStorage) return;
|
|
14456
14407
|
const savePromise = (async () => {
|
|
14457
14408
|
var _a2;
|
|
14458
14409
|
try {
|
|
14459
|
-
const arrayBuffer = await blob.arrayBuffer();
|
|
14460
14410
|
await this.chunkStorage.saveChunk({
|
|
14461
14411
|
proctoringId: this.proctoringId,
|
|
14462
14412
|
chunkIndex: this.chunkIndex,
|
|
14463
|
-
|
|
14413
|
+
blob,
|
|
14464
14414
|
timestamp: Date.now(),
|
|
14465
14415
|
uploaded: 0,
|
|
14466
14416
|
mimeType: ((_a2 = this.recorderOptions) == null ? void 0 : _a2.mimeType) || "video/webm"
|