diff --git a/app/src/main/java/com/futo/platformplayer/api/media/CachedPlatformClient.kt b/app/src/main/java/com/futo/platformplayer/api/media/CachedPlatformClient.kt
index 6f96fc2de72551283505f91ac806796df0b4365e..c604a7e63160223f714eb87023b78c4a897d4ae0 100644
--- a/app/src/main/java/com/futo/platformplayer/api/media/CachedPlatformClient.kt
+++ b/app/src/main/java/com/futo/platformplayer/api/media/CachedPlatformClient.kt
@@ -10,6 +10,7 @@ import com.futo.platformplayer.api.media.models.contents.IPlatformContentDetails
 import com.futo.platformplayer.api.media.models.live.ILiveChatWindowDescriptor
 import com.futo.platformplayer.api.media.models.live.IPlatformLiveEvent
 import com.futo.platformplayer.api.media.models.playback.IPlaybackTracker
+import com.futo.platformplayer.api.media.models.playlists.IPlatformPlaylist
 import com.futo.platformplayer.api.media.models.playlists.IPlatformPlaylistDetails
 import com.futo.platformplayer.api.media.structures.IPager
 import com.futo.platformplayer.models.ImageVariable
@@ -60,6 +61,8 @@ class CachedPlatformClient : IPlatformClient {
         filters: Map<String, List<String>>?
     ): IPager<IPlatformContent> = _client.getChannelContents(channelUrl);
 
+    override fun getChannelPlaylists(channelUrl: String): IPager<IPlatformPlaylist> = _client.getChannelPlaylists(channelUrl);
+
     override fun getPeekChannelTypes(): List<String> = _client.getPeekChannelTypes();
     override fun peekChannelContents(channelUrl: String, type: String?): List<IPlatformContent> = _client.peekChannelContents(channelUrl, type);
 
diff --git a/app/src/main/java/com/futo/platformplayer/api/media/IPlatformClient.kt b/app/src/main/java/com/futo/platformplayer/api/media/IPlatformClient.kt
index fd09adb30c045c3c0230aa8b68d07a7fcf171613..9aaf08c46c04078e017be53a97b765d8bffb4a94 100644
--- a/app/src/main/java/com/futo/platformplayer/api/media/IPlatformClient.kt
+++ b/app/src/main/java/com/futo/platformplayer/api/media/IPlatformClient.kt
@@ -10,6 +10,7 @@ import com.futo.platformplayer.api.media.models.contents.IPlatformContentDetails
 import com.futo.platformplayer.api.media.models.live.ILiveChatWindowDescriptor
 import com.futo.platformplayer.api.media.models.live.IPlatformLiveEvent
 import com.futo.platformplayer.api.media.models.playback.IPlaybackTracker
+import com.futo.platformplayer.api.media.models.playlists.IPlatformPlaylist
 import com.futo.platformplayer.api.media.models.playlists.IPlatformPlaylistDetails
 import com.futo.platformplayer.api.media.structures.IPager
 import com.futo.platformplayer.models.ImageVariable
@@ -93,6 +94,11 @@ interface IPlatformClient {
      */
     fun peekChannelContents(channelUrl: String, type: String? = null): List<IPlatformContent>
 
+    /**
+     * Gets all playlists of a channel
+     */
+    fun getChannelPlaylists(channelUrl: String): IPager<IPlatformPlaylist>
+
     /**
      * Gets the channel url associated with a claimType
      */
diff --git a/app/src/main/java/com/futo/platformplayer/api/media/PlatformClientCapabilities.kt b/app/src/main/java/com/futo/platformplayer/api/media/PlatformClientCapabilities.kt
index 519588125e235d3c12e5328eb3f5ca8fa1e8149e..1b76ae2898efd77d06bfb62454f0348dc58beebf 100644
--- a/app/src/main/java/com/futo/platformplayer/api/media/PlatformClientCapabilities.kt
+++ b/app/src/main/java/com/futo/platformplayer/api/media/PlatformClientCapabilities.kt
@@ -18,7 +18,8 @@ data class PlatformClientCapabilities(
     val hasGetLiveEvents: Boolean = false,
     val hasGetLiveChatWindow: Boolean = false,
     val hasGetContentChapters: Boolean = false,
-    val hasPeekChannelContents: Boolean = false
+    val hasPeekChannelContents: Boolean = false,
+    val hasGetChannelPlaylists: Boolean = false
 ) {
 
 }
\ No newline at end of file
diff --git a/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/JSClient.kt b/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/JSClient.kt
index 641767ea0b9585addb3c73cb3dc824fc88bad63c..464b9117c53fc331e5390f757170a9304e6e5b92 100644
--- a/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/JSClient.kt
+++ b/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/JSClient.kt
@@ -20,6 +20,7 @@ import com.futo.platformplayer.api.media.models.contents.IPlatformContentDetails
 import com.futo.platformplayer.api.media.models.live.ILiveChatWindowDescriptor
 import com.futo.platformplayer.api.media.models.live.IPlatformLiveEvent
 import com.futo.platformplayer.api.media.models.playback.IPlaybackTracker
+import com.futo.platformplayer.api.media.models.playlists.IPlatformPlaylist
 import com.futo.platformplayer.api.media.models.playlists.IPlatformPlaylistDetails
 import com.futo.platformplayer.api.media.platforms.js.internal.JSCallDocs
 import com.futo.platformplayer.api.media.platforms.js.internal.JSDocs
@@ -39,6 +40,7 @@ import com.futo.platformplayer.api.media.platforms.js.models.JSLiveChatWindowDes
 import com.futo.platformplayer.api.media.platforms.js.models.JSLiveEventPager
 import com.futo.platformplayer.api.media.platforms.js.models.JSPlaybackTracker
 import com.futo.platformplayer.api.media.platforms.js.models.JSPlaylistDetails
+import com.futo.platformplayer.api.media.platforms.js.models.JSPlaylistPager
 import com.futo.platformplayer.api.media.structures.EmptyPager
 import com.futo.platformplayer.api.media.structures.IPager
 import com.futo.platformplayer.constructs.Event1
@@ -231,7 +233,8 @@ open class JSClient : IPlatformClient {
             hasGetLiveEvents = plugin.executeBoolean("!!source.getLiveEvents") ?: false,
             hasGetLiveChatWindow = plugin.executeBoolean("!!source.getLiveChatWindow") ?: false,
             hasGetContentChapters = plugin.executeBoolean("!!source.getContentChapters") ?: false,
-            hasPeekChannelContents = plugin.executeBoolean("!!source.peekChannelContents") ?: false
+            hasPeekChannelContents = plugin.executeBoolean("!!source.peekChannelContents") ?: false,
+            hasGetChannelPlaylists = plugin.executeBoolean("!!source.getChannelPlaylists") ?: false
         );
 
         try {
@@ -402,6 +405,14 @@ open class JSClient : IPlatformClient {
             plugin.executeTyped("source.getChannelContents(${Json.encodeToString(channelUrl)}, ${Json.encodeToString(type)}, ${Json.encodeToString(order)}, ${Json.encodeToString(filters)})"));
     }
 
+    @JSDocs(10, "source.getChannelPlaylists(url)", "Gets playlists of a channel")
+    @JSDocsParameter("channelUrl", "A channel url (this platform)")
+    override fun getChannelPlaylists(channelUrl: String): IPager<IPlatformPlaylist> = isBusyWith("getChannelPlaylists") {
+        ensureEnabled();
+        return@isBusyWith JSPlaylistPager(config, this,
+            plugin.executeTyped("source.getChannelPlaylists(${Json.encodeToString(channelUrl)})"));
+    }
+
     @JSDocs(10, "source.getPeekChannelTypes()", "Gets types this plugin has for peek channel contents")
     override fun getPeekChannelTypes(): List<String> {
         if(!capabilities.hasPeekChannelContents)
@@ -423,6 +434,7 @@ open class JSClient : IPlatformClient {
             return listOf();
         }
     }
+
     @JSDocs(10, "source.peekChannelContents(url, type)", "Peek contents of a channel (reverse chronological order)")
     @JSDocsParameter("channelUrl", "A channel url (this platform)")
     @JSDocsParameter("type", "(optional) Type of contents to get from channel")
diff --git a/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/JSChannel.kt b/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/JSChannel.kt
index 014360080edeb886e50688c666b5ed47cad6e5f5..ee0b15cbb10eacb2f081d3452d3e88f7e8573ab7 100644
--- a/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/JSChannel.kt
+++ b/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/JSChannel.kt
@@ -7,6 +7,7 @@ import com.futo.platformplayer.api.media.models.channels.IPlatformChannel
 import com.futo.platformplayer.api.media.models.contents.IPlatformContent
 import com.futo.platformplayer.api.media.platforms.js.SourcePluginConfig
 import com.futo.platformplayer.api.media.structures.IPager
+import com.futo.platformplayer.getOrDefault
 import com.futo.platformplayer.getOrDefaultList
 import com.futo.platformplayer.getOrThrow
 import com.futo.platformplayer.getOrThrowNullable
@@ -37,7 +38,7 @@ class JSChannel : IPlatformChannel {
         description = _channel.getOrThrowNullable(config, "description", contextName);
         url = _channel.getOrThrow(config, "url", contextName);
         urlAlternatives = _channel.getOrDefaultList(config, "urlAlternatives", contextName, listOf()) ?: listOf();
-        links = HashMap();
+        links = HashMap(_channel.getOrDefault<Map<String, String>>(config, "links", contextName, mapOf()) ?: mapOf());
     }
 
     override fun getContents(client: IPlatformClient): IPager<IPlatformContent> {
diff --git a/app/src/main/java/com/futo/platformplayer/states/StatePlatform.kt b/app/src/main/java/com/futo/platformplayer/states/StatePlatform.kt
index 0b03bab16b715f18e7369acadc0d5d52d09a0086..dbfa8886effc284f3b32b14733002d83714911cf 100644
--- a/app/src/main/java/com/futo/platformplayer/states/StatePlatform.kt
+++ b/app/src/main/java/com/futo/platformplayer/states/StatePlatform.kt
@@ -22,6 +22,7 @@ import com.futo.platformplayer.api.media.models.contents.PlatformContentPlacehol
 import com.futo.platformplayer.api.media.models.live.ILiveChatWindowDescriptor
 import com.futo.platformplayer.api.media.models.live.IPlatformLiveEvent
 import com.futo.platformplayer.api.media.models.playback.IPlaybackTracker
+import com.futo.platformplayer.api.media.models.playlists.IPlatformPlaylist
 import com.futo.platformplayer.api.media.models.playlists.IPlatformPlaylistDetails
 import com.futo.platformplayer.api.media.models.video.IPlatformVideo
 import com.futo.platformplayer.api.media.platforms.js.DevJSClient
@@ -799,6 +800,11 @@ class StatePlatform {
         return client.getChannelContents(channelUrl, type, ordering) ;
     }
 
+    fun getChannelPlaylists(channelUrl: String): IPager<IPlatformPlaylist> {
+        val client = getChannelClient(channelUrl);
+        return client.getChannelPlaylists(channelUrl);
+    }
+
     fun peekChannelContents(baseClient: IPlatformClient, channelUrl: String, type: String?): List<IPlatformContent> {
         val client = _channelClientPool.getClientPooled(baseClient, Settings.instance.subscriptions.getSubscriptionsConcurrency());
         return client.peekChannelContents(channelUrl, type) ;
diff --git a/app/src/unstable/assets/sources/youtube b/app/src/unstable/assets/sources/youtube
index 37e2ed94384ff82f4cb67a2250877cb1e8e03c57..d0cca1ac04a1812414ee633a08585dc896701a32 160000
--- a/app/src/unstable/assets/sources/youtube
+++ b/app/src/unstable/assets/sources/youtube
@@ -1 +1 @@
-Subproject commit 37e2ed94384ff82f4cb67a2250877cb1e8e03c57
+Subproject commit d0cca1ac04a1812414ee633a08585dc896701a32