Touffy / client-zip

A client-side streaming ZIP generator
MIT License
349 stars 22 forks source link

[BUG] Zipped file is empty #79

Closed mrtnvgr closed 10 months ago

mrtnvgr commented 11 months ago

Describe the bug

Hi! For some reason my zipped files are always empty.

To Reproduce

Code snippet ```js import { downloadZip } from "https://cdn.jsdelivr.net/npm/client-zip/index.js"; async function initRecorder() { window._recordings = []; window._chunks = []; try { let audio_stream = await navigator.mediaDevices.getUserMedia({ audio: true }); window._recorder = new MediaRecorder(audio_stream); } catch (e) { console.log(e); alert("Failed to get microphone permissions\nYour answers won't be recorded"); return; } _recorder.ondataavailable = (e) => { _chunks.push(e.data); }; _recorder.onstop = (e) => { let blob = new Blob(_chunks); let task_directory = `Task ${current_task}`; let count = 0; for (let recording of _recordings) { if (recording.name.includes(task_directory)) { count++; } } _recordings.push({ name: `${task_directory}/${count+1}.ogg`, input: blob, }) _chunks = []; }; } async function showDownloadPage() { console.log(_recordings); let blob = await downloadZip(_recordings).blob(); console.log(blob); ... } ```

Expected behavior A clear and concise description of what you expected to happen.

Screenshots 20231203_19h25m50s_grim

Desktop:

Additional context Add any other context about the problem here.

mrtnvgr commented 11 months ago

Oh, thats interesting. It seems that multiple files zipping works just fine 🤔

20231203_20h52m20s_grim

Touffy commented 11 months ago

So… does the empty archive occur only when you add a single file ?

I can't spot anything wrong with your code, anyway.

mrtnvgr commented 11 months ago

So… does the empty archive occur only when you add a single file ?

I can't spot anything wrong with your code, anyway.

Yep

mrtnvgr commented 11 months ago

When debugging I found out that _recordings.length appears to be incorrect for some reason...

async function showDownloadPage() {
    console.log(_recordings);

    // TODO: https://github.com/Touffy/client-zip/issues/79
    if (_recordings.length == 1) {
        _recordings.push({ name: "ignore_me", input: "nothing" });
        console.log(_recordings);
    } else {
        console.warn(_recordings.length);
    }

    let blob = await downloadZip(_recordings).blob();
    console.log(blob);
        ...

20231206_20h51m12s_grim

...
console.warn(_recordings.length, _recordings);
...

20231206_21h04m07s_grim

mrtnvgr commented 11 months ago

@Touffy I'm not good in javascript, would you mind helping me please?

Touffy commented 10 months ago

The issue seems to be with the _recordings array. Because it has apparently a length of 0, even though it contains an item. There may be some corruption of Array.prototype in your surrounding code, or something weird when _recordings is created. I cannot understand the issue without seeing more code.

One way to get that strange behaviour would be to set _recordings.length to zero explicitly… perhaps somewhere in a condition you wrote _recordings.length = 0 instead of _recordings.length == 0 ? (though that should also remove the item) Nevermind, there is clearly no such thing in your code. So what's left is some other library on your page doing something weird to Array.prototype.

mrtnvgr commented 10 months ago

The issue seems to be with the _recordings array. Because it has apparently a length of 0, even though it contains an item. There may be some corruption of Array.prototype in your surrounding code, or something weird when _recordings is created. I cannot understand the issue without seeing more code.

One way to get that strange behaviour would be to set _recordings.length to zero explicitly… perhaps somewhere in a condition you wrote _recordings.length = 0 instead of _recordings.length == 0 ? (though that should also remove the item)

Thanks for your help, here is the full file

Touffy commented 10 months ago

Ah ! I think I found it. You are calling showDownloadPage() too soon. _recordings is empty when the archive is created (set a breakpoint at the call to downloadZip — after the await — and you will see).

But console.log is somewhat asynchronous when you give it an object, and shows the contents of _recordings at the time when the browser actually renders the message, not the instant when you called console.log. In that tiny amount of time, _recordings has been updated, so you see an array with one item in your console.

Touffy commented 10 months ago
breakpoint

The _recorder.onstop event is triggered immediately after that, and that's where you push the recording to the array.

mrtnvgr commented 10 months ago

Oh, thank you so much! :heart:

What is the best way to fix this? To wait until the event hooks are finished their tasks? How can I do it?

mrtnvgr commented 10 months ago

Maybe a custom ~state~ attribute, something like processing?

mrtnvgr commented 10 months ago

All fixed now, thanks again!