diff --git a/patches/ffmpeg.diff b/patches/ffmpeg.diff
index 692108aa..c4e3f962 100644
--- a/patches/ffmpeg.diff
+++ b/patches/ffmpeg.diff
@@ -49,10 +49,10 @@ index 0000000..c2a8933
 +#include "../cmdutils.h"
 diff --git a/fftools/ffmpeg-5.1.3/ffmpeg.c b/fftools/ffmpeg-5.1.3/ffmpeg.c
 new file mode 100644
-index 0000000..70ef330
+index 0000000..1056c23
 --- /dev/null
 +++ b/fftools/ffmpeg-5.1.3/ffmpeg.c
-@@ -0,0 +1,4608 @@
+@@ -0,0 +1,4612 @@
 +/*
 + * Copyright (c) 2000-2003 Fabrice Bellard
 + *
@@ -4596,8 +4596,12 @@ index 0000000..70ef330
 +#endif
 +}
 +
++#ifdef __EMSCRIPTEN__
 +int ffmpeg_main(int argc, char **argv);
 +int ffmpeg_main(int argc, char **argv)
++#else
++int main(int argc, char **argv)
++#endif
 +{
 +    int i, ret;
 +    BenchmarkTimeStamps ti;
@@ -11555,7 +11559,7 @@ index 0000000..9d4ef61
 @@ -0,0 +1 @@
 +#include "../opt_common.h"
 diff --git a/fftools/ffmpeg.c b/fftools/ffmpeg.c
-index d721a5e..8469c1e 100644
+index d721a5e..92e53ab 100644
 --- a/fftools/ffmpeg.c
 +++ b/fftools/ffmpeg.c
 @@ -1,3 +1,6 @@
@@ -11599,17 +11603,20 @@ index d721a5e..8469c1e 100644
  }
  
  /* iterate over all output streams in all output files;
-@@ -4135,7 +4156,8 @@ static int64_t getmaxrss(void)
+@@ -4135,7 +4156,12 @@ static int64_t getmaxrss(void)
  #endif
  }
  
--int main(int argc, char **argv)
++#ifdef __EMSCRIPTEN__
 +int ffmpeg_main(int argc, char **argv);
 +int ffmpeg_main(int argc, char **argv)
++#else
+ int main(int argc, char **argv)
++#endif
  {
      int ret;
      BenchmarkTimeStamps ti;
-@@ -4194,3 +4216,4 @@ int main(int argc, char **argv)
+@@ -4194,3 +4220,4 @@ int main(int argc, char **argv)
      exit_program(received_nb_signals ? 255 : main_return_code);
      return main_return_code;
  }
@@ -11707,14 +11714,14 @@ index 055275d..4fea561 100644
  };
 +#endif
 diff --git a/fftools/ffprobe.c b/fftools/ffprobe.c
-index af927cb..b550b27 100644
+index af927cb..12cc546 100644
 --- a/fftools/ffprobe.c
 +++ b/fftools/ffprobe.c
 @@ -92,8 +92,11 @@ typedef struct InputFile {
      int       nb_streams;
  } InputFile;
  
-+#if 0
++#ifndef __EMSCRIPTEN__
 +/* Disabled so that we can have both mains in one program */
  const char program_name[] = "ffprobe";
  const int program_birth_year = 2007;
@@ -11827,7 +11834,7 @@ index af927cb..b550b27 100644
      return 0;
  }
  
-+#if 0
++#ifndef __EMSCRIPTEN__
 +/* Disabled so that we can have both mains in one program */
  void show_help_default(const char *opt, const char *arg)
  {
@@ -11840,17 +11847,20 @@ index af927cb..b550b27 100644
  
  /**
   * Parse interval specification, according to the format:
-@@ -4050,7 +4118,8 @@ static inline int check_section_show_entries(int section_id)
+@@ -4050,7 +4118,12 @@ static inline int check_section_show_entries(int section_id)
              do_show_##varname = 1;                                      \
      } while (0)
  
--int main(int argc, char **argv)
++#ifdef __EMSCRIPTEN__
 +int ffprobe_main(int argc, char **argv);
 +int ffprobe_main(int argc, char **argv)
++#else
+ int main(int argc, char **argv)
++#endif
  {
      const Writer *w;
      WriterContext *wctx;
-@@ -4201,5 +4270,6 @@ end:
+@@ -4201,5 +4274,6 @@ end:
  
      avformat_network_deinit();
  
@@ -11872,15 +11882,19 @@ index a1ab4ce..7c707b3 100644
  }
 +#endif
 diff --git a/libavcodec/libopenh264enc.c b/libavcodec/libopenh264enc.c
-index 8b4755f..da39340 100644
+index 8b4755f..9961b00 100644
 --- a/libavcodec/libopenh264enc.c
 +++ b/libavcodec/libopenh264enc.c
-@@ -378,7 +378,7 @@ static int svc_encode_frame(AVCodecContext *avctx, AVPacket *avpkt,
+@@ -378,7 +378,11 @@ static int svc_encode_frame(AVCodecContext *avctx, AVPacket *avpkt,
      sp.iPicHeight = avctx->height;
  
      if (frame->pict_type == AV_PICTURE_TYPE_I) {
 -        (*s->encoder)->ForceIntraFrame(s->encoder, true);
-+        (*s->encoder)->ForceIntraFrame(s->encoder, true, -1);
++        (*s->encoder)->ForceIntraFrame(s->encoder, true
++#ifdef __EMSCRIPTEN__
++            , -1
++#endif
++        );
      }
  
      encoded = (*s->encoder)->EncodeFrame(s->encoder, &sp, &fbi);
@@ -11915,23 +11929,26 @@ index 47bbbbf..d44d398 100644
  OBJS-$(CONFIG_LIBAMQP_PROTOCOL)          += libamqp.o urldecode.o
  OBJS-$(CONFIG_LIBRIST_PROTOCOL)          += librist.o
 diff --git a/libavformat/file.c b/libavformat/file.c
-index cbdf48d..aeb3767 100644
+index cbdf48d..b45dd03 100644
 --- a/libavformat/file.c
 +++ b/libavformat/file.c
-@@ -41,6 +41,8 @@
+@@ -41,6 +41,10 @@
  #include "os_support.h"
  #include "url.h"
  
++#ifdef __EMSCRIPTEN__
 +#include <emscripten.h>
++#endif
 +
  /* Some systems may not have S_ISFIFO */
  #ifndef S_ISFIFO
  #  ifdef S_IFIFO
-@@ -135,12 +137,34 @@ static const AVClass fd_class = {
+@@ -135,12 +139,38 @@ static const AVClass fd_class = {
      .version    = LIBAVUTIL_VERSION_INT,
  };
  
 +/* libav.js */
++#ifdef __EMSCRIPTEN__
 +EM_JS(void, libavjs_wait_reader, (int fd), {
 +    return Asyncify.handleAsync(function() {
 +        return new Promise(function(res) {
@@ -11942,6 +11959,7 @@ index cbdf48d..aeb3767 100644
 +        });
 +    });
 +});
++#endif
 +/* /libav.js */
 +
  static int file_read(URLContext *h, unsigned char *buf, int size)
@@ -11952,11 +11970,13 @@ index cbdf48d..aeb3767 100644
      ret = read(c->fd, buf, size);
 +
 +    /* libav.js */
++#ifdef __EMSCRIPTEN__
 +    while (ret < 0 && errno == EAGAIN) {
 +        /* wait for more data */
 +        libavjs_wait_reader(c->fd);
 +        ret = read(c->fd, buf, size);
 +    }
++#endif
 +    /* /libav.js */
 +
      if (ret == 0 && c->follow)
@@ -11975,6 +11995,202 @@ index 8a96a37..ede65d6 100644
      } else
          return AVERROR_INVALIDDATA;
  
+diff --git a/libavformat/jsfetch.c b/libavformat/jsfetch.c
+new file mode 100644
+index 0000000..07bc47c
+--- /dev/null
++++ b/libavformat/jsfetch.c
+@@ -0,0 +1,190 @@
++/*
++ * JavaScript fetch metaprotocol for ffmpeg client
++ * Copyright (c) 2023 Yahweasel and contributors
++ *
++ * This file is part of FFmpeg in libav.js. The following license applies only
++ * to this file.
++ *
++ * Permission to use, copy, modify, and/or distribute this software for any
++ * purpose with or without fee is hereby granted.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
++ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
++ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
++ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
++ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
++ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
++ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++ */
++
++#include "config.h"
++#include "config_components.h"
++
++#include "libavutil/error.h"
++#include "libavutil/opt.h"
++
++#include "url.h"
++
++#ifdef __EMSCRIPTEN__
++#include <emscripten.h>
++#endif
++#include <errno.h>
++
++typedef struct JSFetchContext {
++    const AVClass *class;
++    // All of the real information is stored in a JavaScript structure
++    int idx;
++} JSFetchContext;
++
++static const AVOption options[] = {
++    { NULL }
++};
++
++#if CONFIG_JSFETCH_PROTOCOL
++static const AVClass jsfetch_context_class = {
++    .class_name = "jsfetch",
++    .item_name = av_default_item_name,
++    .option = options,
++    .version = LIBAVUTIL_VERSION_INT
++};
++
++/**
++ * Open a fetch connection (JavaScript side).
++ */
++EM_JS(int, jsfetch_open_js, (const char *url), {
++    return Asyncify.handleAsync(function() {
++        return Promise.all([]).then(function() {
++            url = UTF8ToString(url);
++            if (url.slice(0, 8) === "jsfetch:")
++                return fetch(url.slice(8));
++            else
++                return fetch(url);
++        }).then(function(response) {
++            if (!Module.libavjsJSFetch)
++                Module.libavjsJSFetch = {ctr: 1, fetches: {}};
++            var jsf = Module.libavjsJSFetch;
++            var idx = jsf.ctr++;
++            var reader = response.body.getReader();
++            var jsfo = jsf.fetches[idx] = {
++                url: url,
++                response: response,
++                reader: reader,
++                next: reader.read().then(function(res) {
++                    jsfo.buf = res;
++                }).catch(function(rej) {
++                    jsfo.rej = rej;
++                }),
++                buf: null,
++                rej: null
++            };
++            return idx;
++        }).catch(function(ex) {
++            Module.fsThrownError = ex;
++            console.error(ex);
++            return -11 /* ECANCELED */;
++        });
++    });
++});
++
++/**
++ * Open a fetch connection.
++ */
++static int jsfetch_open(URLContext *h, const char *url, int flags, AVDictionary **options)
++{
++    JSFetchContext *ctx = h->priv_data;
++    h->is_streamed = 1;
++    ctx->idx = jsfetch_open_js(url);
++    return (ctx->idx > 0) ? 0 : ctx->idx;
++}
++
++/**
++ * Read from a fetch connection (JavaScript side).
++ */
++EM_JS(int, jsfetch_read_js, (int idx, unsigned char *toBuf, int size), {
++    var jsfo = Module.libavjsJSFetch.fetches[idx];
++    return Asyncify.handleAsync(function() { return Promise.all([]).then(function() {
++        if (jsfo.buf || jsfo.rej) {
++            // Already have data
++            var fromBuf = jsfo.buf;
++            var rej = jsfo.rej;
++
++            if (fromBuf) {
++                if (fromBuf.done) {
++                    // EOF
++                    return -0x20464f45 /* AVERROR_EOF */;
++                }
++                if (fromBuf.value.length > size) {
++                    // Return some of the buffer
++                    Module.HEAPU8.set(fromBuf.value.subarray(0, size), toBuf);
++                    fromBuf.value = fromBuf.value.subarray(size);
++                    return size;
++                }
++
++                /* Otherwise, return the remainder of the buffer and start
++                 * the next read */
++                var ret = fromBuf.value.length;
++                Module.HEAPU8.set(fromBuf.value, toBuf);
++                jsfo.buf = jsfo.rej = null;
++                jsfo.next = jsfo.reader.read().then(function(res) {
++                    jsfo.buf = res;
++                }).catch(function(rej) {
++                    jsfo.rej = rej;
++                });
++                return ret;
++            }
++
++            // Otherwise, there was an error
++            Module.fsThrownError = rej;
++            console.error(rej);
++            return -11 /* ECANCELED */;
++        }
++
++        // The next data isn't available yet. Force them to wait.
++        return Promise.race([
++            jsfo.next,
++            new Promise(function(res) { setTimeout(res, 100); })
++        ]).then(function() { return -6 /* EAGAIN */; });
++    }); });
++});
++
++/**
++ * Read from a fetch connection.
++ */
++static int jsfetch_read(URLContext *h, unsigned char *buf, int size)
++{
++    JSFetchContext *ctx = h->priv_data;
++    return jsfetch_read_js(ctx->idx, buf, size);
++}
++
++/**
++ * Close a fetch connection (JavaScript side).
++ */
++EM_JS(void, jsfetch_close_js, (int idx), {
++    var jsfo = Module.libavjsJSFetch.fetches[idx];
++    if (jsfo) {
++        try { jsfo.reader.cancel(); } catch (ex) {}
++        delete Module.libavjsJSFetch.fetches[idx];
++    }
++});
++
++/**
++ * Close a fetch connection.
++ */
++static int jsfetch_close(URLContext *h)
++{
++    JSFetchContext *ctx = h->priv_data;
++    jsfetch_close_js(ctx->idx);
++    return 0;
++}
++
++const URLProtocol ff_jsfetch_protocol = {
++    .name               = "jsfetch",
++    .url_open2          = jsfetch_open,
++    .url_read           = jsfetch_read,
++    .url_close          = jsfetch_close,
++    .priv_data_size     = sizeof(JSFetchContext),
++    .priv_data_class    = &jsfetch_context_class,
++    .flags              = URL_PROTOCOL_FLAG_NETWORK,
++    .default_whitelist  = "jsfetch,http,https"
++};
++#endif
 diff --git a/libavformat/oggdec.c b/libavformat/oggdec.c
 index 3b19e0b..474d82d 100644
 --- a/libavformat/oggdec.c