diff --git a/app/src/main/java/com/futo/platformplayer/api/media/models/modifier/AdhocRequestModifier.kt b/app/src/main/java/com/futo/platformplayer/api/media/models/modifier/AdhocRequestModifier.kt
new file mode 100644
index 0000000000000000000000000000000000000000..ea250ae450db058a52164ac4c77788356fcf028a
--- /dev/null
+++ b/app/src/main/java/com/futo/platformplayer/api/media/models/modifier/AdhocRequestModifier.kt
@@ -0,0 +1,14 @@
+package com.futo.platformplayer.api.media.models.modifier
+
+class AdhocRequestModifier: IRequestModifier {
+    val _handler: (String, Map<String,String>)->IRequest;
+    override var allowByteSkip: Boolean = false;
+
+    constructor(modifyReq: (String, Map<String,String>)->IRequest) {
+        _handler = modifyReq;
+    }
+
+    override fun modifyRequest(url: String, headers: Map<String, String>): IRequest {
+        return _handler(url, headers);
+    }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/futo/platformplayer/api/media/models/modifier/IModifierOptions.kt b/app/src/main/java/com/futo/platformplayer/api/media/models/modifier/IModifierOptions.kt
new file mode 100644
index 0000000000000000000000000000000000000000..1a7e4ce6b0933395be00b23224d6ae779f8608e4
--- /dev/null
+++ b/app/src/main/java/com/futo/platformplayer/api/media/models/modifier/IModifierOptions.kt
@@ -0,0 +1,6 @@
+package com.futo.platformplayer.api.media.models.modifier
+
+interface IModifierOptions {
+    val applyAuthClient: String?;
+    val applyCookieClient: String?;
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/futo/platformplayer/api/media/models/modifier/IRequest.kt b/app/src/main/java/com/futo/platformplayer/api/media/models/modifier/IRequest.kt
new file mode 100644
index 0000000000000000000000000000000000000000..43cd502c1ba495e247e77bcd1e661117329d991c
--- /dev/null
+++ b/app/src/main/java/com/futo/platformplayer/api/media/models/modifier/IRequest.kt
@@ -0,0 +1,6 @@
+package com.futo.platformplayer.api.media.models.modifier
+
+interface IRequest {
+    val url: String?;
+    val headers: Map<String, String>;
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/futo/platformplayer/api/media/models/modifier/IRequestModifier.kt b/app/src/main/java/com/futo/platformplayer/api/media/models/modifier/IRequestModifier.kt
new file mode 100644
index 0000000000000000000000000000000000000000..f15ee4771eba3fe9f6bd517d86b8a966a1acad31
--- /dev/null
+++ b/app/src/main/java/com/futo/platformplayer/api/media/models/modifier/IRequestModifier.kt
@@ -0,0 +1,7 @@
+package com.futo.platformplayer.api.media.models.modifier
+
+
+interface IRequestModifier {
+    var allowByteSkip: Boolean;
+    fun modifyRequest(url: String, headers: Map<String, String>): IRequest
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/internal/JSHttpClient.kt b/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/internal/JSHttpClient.kt
index 3175201afed6ceba917104c9ec1ab62551b59527..8496dfc788b40a331c0eb7291d201c0c059585da 100644
--- a/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/internal/JSHttpClient.kt
+++ b/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/internal/JSHttpClient.kt
@@ -57,11 +57,6 @@ class JSHttpClient : ManagedHttpClient {
         return newClient;
     }
 
-    fun applyRequest(req: JSRequestModifier.Request) {
-
-
-    }
-
     //TODO: Use this in beforeRequest to remove dup code
     fun applyHeaders(url: Uri, headers: MutableMap<String, String>, applyAuth: Boolean = false, applyOtherCookies: Boolean = false) {
         val domain = url.host!!.lowercase();
diff --git a/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/JSRequest.kt b/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/JSRequest.kt
index 21b0a5de642298442cec7894a66f4ff53592af37..1a9ef32b4fd0218e8bde298ff41b13b9ab0542f5 100644
--- a/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/JSRequest.kt
+++ b/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/JSRequest.kt
@@ -1,18 +1,81 @@
 package com.futo.platformplayer.api.media.platforms.js.models
 
+import android.net.Uri
 import com.caoccao.javet.values.reference.V8ValueObject
+import com.futo.platformplayer.api.media.models.modifier.IModifierOptions
+import com.futo.platformplayer.api.media.models.modifier.IRequest
+import com.futo.platformplayer.api.media.platforms.js.JSClient
 import com.futo.platformplayer.engine.IV8PluginConfig
-import com.futo.platformplayer.getOrThrow
-import com.futo.platformplayer.logging.Logger
+import com.futo.platformplayer.getOrDefault
 
 @kotlinx.serialization.Serializable
-class JSRequest : JSRequestModifier.IRequest {
-    override val url: String;
-    override var headers: Map<String, String>;
+class JSRequest : IRequest {
+    private val _v8Url: String?;
+    private val _v8Headers: Map<String, String>?;
+    private val _v8Options: Options?;
 
-    constructor(config: IV8PluginConfig, obj: V8ValueObject) {
+    override var url: String? = null;
+    override lateinit var headers: Map<String, String>;
+
+    constructor(plugin: JSClient, url: String?, headers: Map<String, String>?, options: Options?, originalUrl: String?, originalHeaders: Map<String, String>?) {
+        _v8Url = url;
+        _v8Headers = headers;
+        _v8Options = options;
+        initialize(plugin, originalUrl, originalHeaders);
+    }
+    constructor(plugin: JSClient, obj: V8ValueObject, originalUrl: String?, originalHeaders: Map<String, String>?) {
         val contextName = "ModifyRequestResponse";
-        url = obj.getOrThrow(config, "url", contextName);
-        headers = obj.getOrThrow(config, "headers", contextName);
+        val config = plugin.config;
+        _v8Url = obj.getOrDefault<String>(config, "url", contextName, null);
+        _v8Headers = obj.getOrDefault<Map<String, String>>(config, "headers", contextName, null);
+        _v8Options = obj.getOrDefault<V8ValueObject>(config, "options", "JSRequestModifier.options", null)?.let {
+            Options(config, it);
+        }
+        initialize(plugin, originalUrl, originalHeaders);
+    }
+
+    private fun initialize(plugin: JSClient, originalUrl: String?, originalHeaders: Map<String, String>?) {
+        val config = plugin.config;
+        url = _v8Url ?: originalUrl;
+        headers = _v8Headers ?: originalHeaders ?: mapOf();
+
+        if(_v8Options != null) {
+            if(_v8Options.applyCookieClient != null && url != null) {
+                val client = plugin.getHttpClientById(_v8Options.applyCookieClient);
+                if(client != null) {
+                    val toModifyHeaders = headers.toMutableMap();
+                    client.applyHeaders(Uri.parse(url), toModifyHeaders, false, true);
+                    headers = toModifyHeaders;
+                }
+            }
+            if(_v8Options.applyAuthClient != null && url != null) {
+                val client = plugin.getHttpClientById(_v8Options.applyAuthClient);
+                if(client != null) {
+                    val toModifyHeaders = headers.toMutableMap();
+                    client.applyHeaders(Uri.parse(url), toModifyHeaders, true, false);
+                    headers = toModifyHeaders;
+                }
+            }
+        }
+    }
+
+    fun modify(plugin: JSClient, originalUrl: String?, originalHeaders: Map<String, String>?): JSRequest {
+        return JSRequest(plugin, _v8Url, _v8Headers, _v8Options, originalUrl, originalHeaders);
+    }
+
+
+    @kotlinx.serialization.Serializable
+    class Options: IModifierOptions {
+        override val applyAuthClient: String?;
+        override val applyCookieClient: String?;
+
+        constructor(config: IV8PluginConfig, obj: V8ValueObject) {
+            applyAuthClient = obj.getOrDefault(config, "applyAuthClient", "JSRequestModifier.options.applyAuthClient", null);
+            applyCookieClient = obj.getOrDefault(config, "applyCookieClient", "JSRequestModifier.options.applyCookieClient", null);
+        }
+    }
+
+    companion object {
+
     }
 }
\ No newline at end of file
diff --git a/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/JSRequestModifier.kt b/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/JSRequestModifier.kt
index d05ef048cbcb795daa518094a3050b5392d26d69..cffbb4743aea52a9e288430113e2fc3bc26b0c87 100644
--- a/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/JSRequestModifier.kt
+++ b/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/JSRequestModifier.kt
@@ -2,6 +2,8 @@ package com.futo.platformplayer.api.media.platforms.js.models
 
 import android.net.Uri
 import com.caoccao.javet.values.reference.V8ValueObject
+import com.futo.platformplayer.api.media.models.modifier.IRequest
+import com.futo.platformplayer.api.media.models.modifier.IRequestModifier
 import com.futo.platformplayer.api.media.platforms.js.JSClient
 import com.futo.platformplayer.engine.IV8PluginConfig
 import com.futo.platformplayer.engine.V8Plugin
@@ -10,11 +12,11 @@ import com.futo.platformplayer.getOrDefault
 import com.futo.platformplayer.getOrNull
 import com.futo.platformplayer.getOrThrow
 
-class JSRequestModifier {
+class JSRequestModifier: IRequestModifier {
     private val _plugin: JSClient;
     private val _config: IV8PluginConfig;
     private var _modifier: V8ValueObject;
-    val allowByteSkip: Boolean;
+    override var allowByteSkip: Boolean;
 
     constructor(plugin: JSClient, modifier: V8ValueObject) {
         this._plugin = plugin;
@@ -28,7 +30,7 @@ class JSRequestModifier {
             throw ScriptImplementationException(config, "RequestModifier is missing modifyRequest", null);
     }
 
-    fun modifyRequest(url: String, headers: Map<String, String>): IRequest {
+    override fun modifyRequest(url: String, headers: Map<String, String>): IRequest {
         if (_modifier.isClosed) {
             return Request(url, headers);
         }
@@ -37,35 +39,10 @@ class JSRequestModifier {
             _modifier.invoke("modifyRequest", url, headers);
         } as V8ValueObject;
 
-        val req = JSRequest(_config, result);
-        val options: V8ValueObject? = result.getOrDefault(_config, "options", "JSRequestModifier.options", null);
-        if(options != null) {
-            if(options.has("applyCookieClient")) {
-                val clientId: String = options.getOrThrow(_config, "applyCookieClient", "JSRequestModifier.options.applyCookieClient", false)
-                val client = _plugin.getHttpClientById(clientId);
-                if(client != null) {
-                    val toModifyHeaders = req.headers.toMutableMap();
-                    client.applyHeaders(Uri.parse(req.url), toModifyHeaders, false, true);
-                    req.headers = toModifyHeaders;
-                }
-            }
-            if(options.has("applyAuthClient")) {
-                val clientId: String = options.getOrThrow(_config, "applyAuthClient", "JSRequestModifier.options.applyAuthClient", false)
-                val client = _plugin.getHttpClientById(clientId);
-                if(client != null) {
-                    val toModifyHeaders = req.headers.toMutableMap();
-                    client.applyHeaders(Uri.parse(req.url), toModifyHeaders, true, false);
-                    req.headers = toModifyHeaders;
-                }
-            }
-        }
+        val req = JSRequest(_plugin, result, url, headers);
         return req;
     }
 
-    interface IRequest {
-        val url: String;
-        val headers: Map<String, String>;
-    }
 
     data class Request(override val url: String, override val headers: Map<String, String>) : IRequest;
 }
\ No newline at end of file
diff --git a/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/sources/JSSource.kt b/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/sources/JSSource.kt
index a6ae5a7c26067eafd319c3dec85a9142c92c3d08..14bed433c14f4773c5ca36cbbe3752eff6927e1c 100644
--- a/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/sources/JSSource.kt
+++ b/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/sources/JSSource.kt
@@ -4,12 +4,16 @@ package com.futo.platformplayer.api.media.platforms.js.models.sources
 
 import com.caoccao.javet.values.V8Value
 import com.caoccao.javet.values.reference.V8ValueObject
+import com.futo.platformplayer.api.media.models.modifier.AdhocRequestModifier
+import com.futo.platformplayer.api.media.models.modifier.IRequestModifier
 import com.futo.platformplayer.api.media.models.streams.sources.IAudioSource
 import com.futo.platformplayer.api.media.models.streams.sources.IVideoSource
 import com.futo.platformplayer.api.media.platforms.js.JSClient
+import com.futo.platformplayer.api.media.platforms.js.models.JSRequest
 import com.futo.platformplayer.api.media.platforms.js.models.JSRequestModifier
 import com.futo.platformplayer.engine.IV8PluginConfig
 import com.futo.platformplayer.engine.V8Plugin
+import com.futo.platformplayer.getOrDefault
 import com.futo.platformplayer.orNull
 import com.futo.platformplayer.views.video.datasources.JSHttpDataSource
 import com.google.android.exoplayer2.upstream.DefaultHttpDataSource
@@ -20,6 +24,7 @@ abstract class JSSource {
     protected val _config: IV8PluginConfig;
     protected val _obj: V8ValueObject;
     val hasRequestModifier: Boolean;
+    private val _requestModifier: JSRequest?;
 
     val type : String;
 
@@ -29,10 +34,18 @@ abstract class JSSource {
         this._obj = obj;
         this.type = type;
 
-        hasRequestModifier = obj.has("getRequestModifier");
+        _requestModifier = obj.getOrDefault<V8ValueObject>(_config, "requestModifier", "JSSource.requestModifier", null)?.let {
+            JSRequest(plugin, it, null, null);
+        }
+        hasRequestModifier = _requestModifier != null || obj.has("getRequestModifier");
     }
 
-    fun getRequestModifier(): JSRequestModifier? {
+    fun getRequestModifier(): IRequestModifier? {
+        if(_requestModifier != null)
+            return AdhocRequestModifier { url, headers ->
+                  return@AdhocRequestModifier _requestModifier.modify(_plugin, url, headers);
+            };
+
         if (!hasRequestModifier || _obj.isClosed) {
             return null;
         }
diff --git a/app/src/main/java/com/futo/platformplayer/views/video/datasources/JSHttpDataSource.java b/app/src/main/java/com/futo/platformplayer/views/video/datasources/JSHttpDataSource.java
index 00a49cf85d8c89f0b6d5f95fb61ea28cdc7a5b12..c6d14cebe7c2cf8c2586113192475bf117a94e05 100644
--- a/app/src/main/java/com/futo/platformplayer/views/video/datasources/JSHttpDataSource.java
+++ b/app/src/main/java/com/futo/platformplayer/views/video/datasources/JSHttpDataSource.java
@@ -9,6 +9,8 @@ import android.net.Uri;
 import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
 
+import com.futo.platformplayer.api.media.models.modifier.IRequest;
+import com.futo.platformplayer.api.media.models.modifier.IRequestModifier;
 import com.futo.platformplayer.api.media.platforms.js.models.JSRequestModifier;
 import com.google.android.exoplayer2.C;
 import com.google.android.exoplayer2.PlaybackException;
@@ -57,7 +59,7 @@ public class JSHttpDataSource extends BaseDataSource implements HttpDataSource {
         private int readTimeoutMs;
         private boolean allowCrossProtocolRedirects;
         private boolean keepPostFor302Redirects;
-        @Nullable private JSRequestModifier requestModifier = null;
+        @Nullable private IRequestModifier requestModifier = null;
 
         /** Creates an instance. */
         public Factory() {
@@ -80,7 +82,7 @@ public class JSHttpDataSource extends BaseDataSource implements HttpDataSource {
          * @param requestModifier The request modifier that will be used, or {@code null} to use no request modifier
          * @return This factory.
          */
-        public Factory setRequestModifier(@Nullable JSRequestModifier requestModifier) {
+        public Factory setRequestModifier(@Nullable IRequestModifier requestModifier) {
             this.requestModifier = requestModifier;
             return this;
         }
@@ -225,7 +227,7 @@ public class JSHttpDataSource extends BaseDataSource implements HttpDataSource {
     private int responseCode;
     private long bytesToRead;
     private long bytesRead;
-    @Nullable private JSRequestModifier requestModifier;
+    @Nullable private IRequestModifier requestModifier;
 
     private JSHttpDataSource(
             @Nullable String userAgent,
@@ -235,7 +237,7 @@ public class JSHttpDataSource extends BaseDataSource implements HttpDataSource {
             @Nullable RequestProperties defaultRequestProperties,
             @Nullable Predicate<String> contentTypePredicate,
             boolean keepPostFor302Redirects,
-            @Nullable JSRequestModifier requestModifier) {
+            @Nullable IRequestModifier requestModifier) {
         super(/* isNetwork= */ true);
         this.userAgent = userAgent;
         this.connectTimeoutMillis = connectTimeoutMillis;
@@ -571,8 +573,9 @@ public class JSHttpDataSource extends BaseDataSource implements HttpDataSource {
 
         String requestUrl = url.toString();
         if (requestModifier != null) {
-            JSRequestModifier.IRequest result = requestModifier.modifyRequest(requestUrl, requestHeaders);
-            requestUrl = result.getUrl();
+            IRequest result = requestModifier.modifyRequest(requestUrl, requestHeaders);
+            String modifiedUrl = result.getUrl();
+            requestUrl = (modifiedUrl != null) ? modifiedUrl : requestUrl;
             requestHeaders = result.getHeaders();
         }
 
diff --git a/app/src/unstable/assets/sources/niconico b/app/src/unstable/assets/sources/niconico
index f2086dc2cf6387f692049f52cdf938313dc53654..f031e10d3852dd7138f2ed312c5e74466f23da32 160000
--- a/app/src/unstable/assets/sources/niconico
+++ b/app/src/unstable/assets/sources/niconico
@@ -1 +1 @@
-Subproject commit f2086dc2cf6387f692049f52cdf938313dc53654
+Subproject commit f031e10d3852dd7138f2ed312c5e74466f23da32
diff --git a/app/src/unstable/assets/sources/youtube b/app/src/unstable/assets/sources/youtube
index fad5e14f84573796b3989794c35f34a1f88f03a6..d41cc8e848891ef8e949e6d49384b754e7c305c7 160000
--- a/app/src/unstable/assets/sources/youtube
+++ b/app/src/unstable/assets/sources/youtube
@@ -1 +1 @@
-Subproject commit fad5e14f84573796b3989794c35f34a1f88f03a6
+Subproject commit d41cc8e848891ef8e949e6d49384b754e7c305c7