Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Bug]: video files not seekable with protocol.handle #38749

Open
3 tasks done
philer opened this issue Jun 13, 2023 · 10 comments
Open
3 tasks done

[Bug]: video files not seekable with protocol.handle #38749

philer opened this issue Jun 13, 2023 · 10 comments
Labels
25-x-y bug 🪲 component/protocol has-repro-gist Issue can be reproduced with code at https://gist.github.com/ platform/all status/confirmed A maintainer reproduced the bug or agreed with the feature

Comments

@philer
Copy link

philer commented Jun 13, 2023

Preflight Checklist

Electron Version

25.1.0

What operating system are you using?

Other Linux

Operating System Version

Linux Mint 21.1

What arch are you using?

x64

Last Known Working Electron version

No response

Expected Behavior

I'm using a custom protocol in place of file:// URLs for a video player. This works:

protocol.registerFileProtocol("video", (request, callback) => {
  callback(request.url.slice("video://".length))  // simplified path for this example
})

Since protocol.registerFileProtocol got deprecated in version 25, I tried the suggested alternative protocol.handle as per the update notes:

protocol.handle("video", (request) => {
  return net.fetch(request.url.replace(/^video:/, "file:"))  // simplified path
})

This allows me to play videos. However now when I set .currentTimeon a <video> element with a video://… source, it just jumps back to zero. video.seekable.end() is always 0. This breaks seeking in HTML5 videos with custom protocol URLs.

I've also tried this, with the same result:

protocol.handle("video", (request) => {
  return new Response(fs.readFileSync(request.url.replace(/^video:/, "file:")))
})

Not sure if it matters, the scheme is registered like this:

protocol.registerSchemesAsPrivileged([
  {
    scheme: "video",
    privileges: {
      secure: true,
      bypassCSP: true,
      stream: true,
    },
  },
])

Expected behavior: The documented alternative to protocol.registerFileProtocol should allow serving seekable video files.

Actual Behavior

See above.

Testcase Gist URL

No response

Additional Information

No response

@philer philer changed the title [Bug]: [Bug]: video files not seekable with protocol.handle Jun 13, 2023
@codebytere codebytere added the blocked/need-repro Needs a test case to reproduce the bug label Jun 13, 2023
@github-actions
Copy link
Contributor

Hello @philer. Thanks for reporting this and helping to make Electron better!

Would it be possible for you to make a standalone testcase with only the code necessary to reproduce the issue? For example, Electron Fiddle is a great tool for making small test cases and makes it easy to publish your test case to a gist that Electron maintainers can use.

Stand-alone test cases make fixing issues go more smoothly: it ensure everyone's looking at the same issue, it removes all unnecessary variables from the equation, and it can also provide the basis for automated regression tests.

Now adding the blocked/need-repro label for this reason. After you make a test case, please link to it in a followup comment. This issue will be closed in 10 days if the above is not addressed.

@philer
Copy link
Author

philer commented Jun 13, 2023

@codebytere I don't think this is something I can reproduce in a fiddle…

@github-actions github-actions bot removed the blocked/need-repro Needs a test case to reproduce the bug label Jun 13, 2023
@takumus
Copy link

takumus commented Jun 14, 2023

Hello!
I have the same problem on Windows.
Video files prepared using protocol.handle cannot be seeked.

Electron: 25.1.0
OS: Windows 11 Pro x64 10.0.22621 Build 22621

Fiddle:
https://gist.github.com/takumus/7b6d73df884df009f36bc6ff1979d5ff

This text was translated by Deepl.

@codebytere codebytere added status/confirmed A maintainer reproduced the bug or agreed with the feature has-repro-gist Issue can be reproduced with code at https://gist.github.com/ platform/all and removed platform/linux labels Jun 14, 2023
@codebytere
Copy link
Member

codebytere commented Jun 15, 2023

@philer the fiddle by @takumus is what i was looking for! i can try to look into this more but @nornagon (who is off work at the moment - back midsummer) may be a better person to investigate depending on how far i get.

@philer
Copy link
Author

philer commented Jun 18, 2023

I see, thanks! 👍

@Tarrowren
Copy link

protocol.handle("https", async (request) => {
    const path = "G:/big_buck_bunny_trailer_480p_logo.webm";
    const stats = await stat(path);

    const headers = new Headers();
    headers.set("Accept-Ranges", "bytes");
    headers.set("Content-Type", "video/webm");

    let status = 200;
    const rangeText = request.headers.get("range");

    let stream;
    if (rangeText) {
        const ranges = parseRangeRequests(rangeText, stats.size);

        const [start, end] = ranges[0];
        console.log(rangeText, stats.size, start, end);
        headers.set("Content-Length", `${end - start + 1}`);
        headers.set("Content-Range", `bytes ${start}-${end}/${stats.size}`);
        status = 206;
        stream = createReadStream(path, { start, end });
    } else {
        headers.set("Content-Length", `${stats.size}`);
        stream = createReadStream(path);
    }

    return new Response(stream, {
        headers,
        status,
    });
});

function parseRangeRequests(text, size) {
    const token = text.split("=");
    if (token.length !== 2 || token[0] !== "bytes") {
        return [];
    }

    return token[1]
        .split(",")
        .map((v) => parseRange(v, size))
        .filter(([start, end]) => !isNaN(start) && !isNaN(end) && start <= end);
}

const NAN_ARRAY = [NaN, NaN];

function parseRange(text, size) {
    const token = text.split("-");
    if (token.length !== 2) {
        return NAN_ARRAY;
    }

    const startText = token[0].trim();
    const endText = token[1].trim();

    if (startText === "") {
        if (endText === "") {
            return NAN_ARRAY;
        } else {
            let start = size - Number(endText);
            if (start < 0) {
                start = 0;
            }

            return [start, size - 1];
        }
    } else {
        if (endText === "") {
            return [Number(startText), size - 1];
        } else {
            let end = Number(endText);
            if (end >= size) {
                end = size - 1;
            }

            return [Number(startText), end];
        }
    }
}

image

@kevinfrei
Copy link

Affects plain audio files, as well. Present on Mac and Windows (haven't tested Linux yet, but if someone wants, just ask)

Workaround is staying on the deprecated API for the time being.

@codebytere
Copy link
Member

@nornagon would you be able to take a look at this one?

@TiddoLangerak
Copy link

Still an issue. Also, similar as to this comment from 2021t, the issue only affects the first load of a video item. I.e. if we close and re-open the video, then suddenly seeking does work.

@vincaslt
Copy link

Also having this issue. Currently registerFileProtocol still works fine as an alternative, however I noticed it triggers false-positive virus warning in windows security making it a hassle to an app using this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
25-x-y bug 🪲 component/protocol has-repro-gist Issue can be reproduced with code at https://gist.github.com/ platform/all status/confirmed A maintainer reproduced the bug or agreed with the feature
Projects
No open projects
Status: Unsorted Items
Development

No branches or pull requests

8 participants