Ok.... this is what I got when fixing it with cursor Appreciate the hard work always!
Fix for older PotPlayer: HostDecodeSigUrl is missing
Diffed against the original PotPlayer-SponsorBlock script (MediaPlayParse - YouTube with SponsorBlock.as).
That is the only functional change — 2,840 lines → 3,112 lines, five hunks, nothing else. SponsorBlock chapters, Innertube, quality list, etc. are untouched.
Problem
On older PotPlayer builds this script never actually runs. The AngelScript console shows:
ERR : No matching symbol 'HostDecodeSigUrl'
HostDecodeSigUrl is a host API that exists only in newer PotPlayer. Without it the parser does not compile, so YouTube URLs fail / flicker through qualities and never play.
The stock bundled MediaPlayParse - YouTube.as already has a local JS signature decoder (SignatureDecode + youtubeFuncType / Delete / Swap / Reverse / GetFunction). I copied that into this SponsorBlock fork and wrapped it as DecodeSigUrl(...) so the three existing call sites could stay the same shape.
Changes
1. Inserted local decoder (before ReplaceCodecName)
Added: youtubeFuncType, Delete, Swap, Reverse, GetFunction, SignatureDecode, and DecodeSigUrl.
DecodeSigUrl is a drop-in for HostDecodeSigUrl(url, cipher, jsData): it parses signatureCipher / cipher (url / s / sp), applies the player-JS transform, and also handles /s/ in dash/HLS URLs.
The full decoder block is in the working file at lines 1021–1289, copied from stock MediaPlayParse - YouTube.as.
2. In PlayitemParse, declare the decoder state next to the other locals
array<youtubeFuncType> JSFuncs;
array<int> JSFuncArgs;
3. Replace the three HostDecodeSigUrl calls
Live dash/HLS:
// was:
liveUrl = HostDecodeSigUrl(liveUrl, "", JSData);
// now:
liveUrl = DecodeSigUrl(liveUrl, "", WebData, JSData, JSFuncs, JSFuncArgs);
Adaptive formats (url or signatureCipher / cipher):
// was:
resolvedUrl = HostDecodeSigUrl(resolvedUrl, cipherText, JSData);
// now:
resolvedUrl = DecodeSigUrl(resolvedUrl, cipherText, WebData, JSData, JSFuncs, JSFuncArgs);
Non-live dash/HLS fallback:
// was:
url = HostDecodeSigUrl(url, "", JSData);
// now:
url = DecodeSigUrl(url, "", WebData, JSData, JSFuncs, JSFuncArgs);
How to patch your own copy
Open MediaPlayParse - YouTube with SponsorBlock.as in a text editor. There are four edits: one insert, one local-variable add, and three one-line replacements. Search for the FIND text; it should match once each time.
When you are done, there must be zero remaining HostDecodeSigUrl in the file. Copy the patched .as into:
PotPlayer\Extension\Media\PlayParse\
Restart PotPlayer (or reload extensions).
Edit 1 — insert the local decoder
FIND this (it is the start of ReplaceCodecName, around line 1021 in the original):
string ReplaceCodecName(string name, string id)
INSERT the entire block below immediately before that line. Do not delete ReplaceCodecName.
// Local signature decoder - HostDecodeSigUrl is not available in all PotPlayer builds
enum youtubeFuncType
{
funcNONE = -1,
funcDELETE,
funcREVERSE,
funcSWAP
};
void Delete(string &a, int b)
{
a.erase(0, b);
}
void Swap(string &a, int b)
{
uint8 c = a[0];
b %= a.size();
a[0] = a[b];
a[b] = c;
};
void Reverse(string &a)
{
int len = a.size();
for (int i = 0; i < len / 2; ++i)
{
uint8 c = a[i];
a[i] = a[len - i - 1];
a[len - i - 1] = c;
}
}
string GetFunction(string str)
{
array<string> signatureRegExps =
{
"(?:\\b|[^a-zA-Z0-9$])([a-zA-Z0-9$]{2,})\\s*=\\s*function\\(\\s*a\\s*\\)\\s*\\{\\s*a\\s*=\\s*a\\.split\\(\\s*\"\"\\s*\\)",
"(?:\\b|[^a-zA-Z0-9$])([a-zA-Z0-9$]{2,})\\s*=\\s*function\\(\\s*a\\s*\\)\\s*\\{\\s*a\\s*=\\s*a\\.split\\(\\s*\"\"\\s*\\);[a-zA-Z0-9$]{2}\\.[a-zA-Z0-9$]{2}\\(a,\\d+\\)",
"\\b[cs]\\s*&&\\s*[adf]\\.set\\([^,]+\\s*,\\s*encodeURIComponent\\s*\\(\\s*([a-zA-Z0-9$]+)\\(",
"\\b[a-zA-Z0-9]+\\s*&&\\s*[a-zA-Z0-9]+\\.set\\([^,]+\\s*,\\s*encodeURIComponent\\s*\\(\\s*([a-zA-Z0-9$]+)\\(",
"([a-zA-Z0-9$]+)\\s*=\\s*function\\(\\s*a\\s*\\)\\s*\\{\\s*a\\s*=\\s*a\\.split\\(\\s*\"\"\\s*\\)",
"([\"\\'])signature\\1\\s*,\\s*([a-zA-Z0-9$]+)\\(",
"\\.sig\\|\\|([a-zA-Z0-9$]+)\\(",
"yt\\.akamaized\\.net/\\)\\s*\\|\\|\\s*.*?\\s*[cs]\\s*&&\\s*[adf]\\.set\\([^,]+\\s*,\\s*(?:encodeURIComponent\\s*\\()?\\s*([a-zA-Z0-9$]+)\\(",
"\\b[cs]\\s*&&\\s*[adf]\\.set\\([^,]+\\s*,\\s*([a-zA-Z0-9$]+)\\(",
"\\b[a-zA-Z0-9]+\\s*&&\\s*[a-zA-Z0-9]+\\.set\\([^,]+\\s*,\\s*([a-zA-Z0-9$]+)\\(",
"\\bc\\s*&&\\s*a\\.set\\([^,]+\\s*,\\s*\\([^)]*\\)\\s*\\(\\s*([a-zA-Z0-9$]+)\\(",
"\\bc\\s*&&\\s*[a-zA-Z0-9]+\\.set\\([^,]+\\s*,\\s*\\([^)]*\\)\\s*\\(\\s*([a-zA-Z0-9$]+)\\(",
"\\bc\\s*&&\\s*[a-zA-Z0-9]+\\.set\\([^,]+\\s*,\\s*\\([^)]*\\)\\s*\\(\\s*([a-zA-Z0-9$]+)\\("
};
for (int i = 0, len = signatureRegExps.size(); i < len; i++)
{
string ret = HostRegExpParse(str, signatureRegExps[i]);
if (!ret.empty()) return ret;
}
string r, sig = "\"signature\"";
int p = 0;
while (true)
{
int e = str.find(sig, p);
if (e < 0) break;
int s1 = str.find("(", e);
int s2 = str.find(")", e);
if (s1 > s2)
{
p = e + 10;
continue;
}
p = e + sig.size() + 1;
r = str.substr(p, s1 - p);
break;
}
r.Trim(",");
r.Trim();
r.Trim(",");
r.Trim();
return r;
}
string SignatureDecode(string url, string signature, string append, string data, string js_data, array<youtubeFuncType> &JSFuncs, array<int> &JSFuncArgs)
{
if (JSFuncs.size() == 0 && !js_data.empty())
{
string funcName = GetFunction(js_data);
if (!funcName.empty())
{
string funcRegExp = funcName + "=function\\(a\\)\\{([^\\n]+)\\};";
string funcBody = HostRegExpParse(data, funcRegExp);
if (funcBody.empty())
{
string varfunc = funcName + "=function(a){";
funcBody = GetEntry(js_data, varfunc, "};");
}
if (!funcBody.empty())
{
string funcGroup;
array<string> funcList;
array<string> funcCodeList;
array<string> code = funcBody.split(";");
for (int i = 0, len = code.size(); i < len; i++)
{
string line = code[i];
if (!line.empty())
{
if (line.find("split") >= 0 || line.find("return") >= 0) continue;
funcList.insertLast(line);
if (funcGroup.empty())
{
int k = line.find(".");
if (k > 0) funcGroup = line.Left(k);
}
}
}
if (!funcGroup.empty())
{
string tmp = GetEntry(js_data, "var " + funcGroup + "={", "};");
if (!tmp.empty())
{
tmp.replace("\n", "");
funcCodeList = tmp.split("},");
}
}
if (!funcList.empty() && !funcCodeList.empty())
{
for (int j = 0, len = funcList.size(); j < len; j++)
{
string func = funcList[j];
if (!func.empty())
{
int funcArg = 0;
string funcArgs = GetEntry(func, "(", ")");
array<string> args = funcArgs.split(",");
if (args.size() >= 1)
{
string arg = args[args.size() - 1];
funcArg = parseInt(arg);
}
string funcName = GetEntry(func, funcGroup + '.', "(");
if (funcName.empty())
{
funcName = GetEntry(func, funcGroup, "(");
if (funcName.empty()) continue;
}
if (funcName.find("[") >= 0)
{
funcName.replace("[", "");
funcName.replace("]", "");
}
funcName += ":function";
youtubeFuncType funcType = youtubeFuncType::funcNONE;
for (int k = 0, len = funcCodeList.size(); k < len; k++)
{
string funcCode = funcCodeList[k];
if (funcCode.find(funcName) >= 0)
{
if (funcCode.find("splice") > 0) funcType = youtubeFuncType::funcDELETE;
else if (funcCode.find("reverse") > 0) funcType = youtubeFuncType::funcREVERSE;
else if (funcCode.find(".length]") > 0) funcType = youtubeFuncType::funcSWAP;
break;
}
}
if (funcType != youtubeFuncType::funcNONE)
{
JSFuncs.insertLast(funcType);
JSFuncArgs.insertLast(funcArg);
}
}
}
}
}
}
}
if (!JSFuncs.empty() && JSFuncs.size() == JSFuncArgs.size())
{
for (int i = 0, len = JSFuncs.size(); i < len; i++)
{
mar youtubeFuncType func = JSFuncs[i];
int arg = JSFuncArgs[i];
switch (func)
{
case youtubeFuncType::funcDELETE:
Delete(signature, arg);
break;
case youtubeFuncType::funcSWAP:
Swap(signature, arg);
break;
case youtubeFuncType::funcREVERSE:
Reverse(signature);
break;
}
}
url = url + append + signature;
}
return url;
}
// Drop-in replacement for HostDecodeSigUrl (missing on older PotPlayer hosts)
string DecodeSigUrl(string url, string cipher, string webData, string jsData, array<youtubeFuncType> &JSFuncs, array<int> &JSFuncArgs)
{
if (!cipher.empty())
{
string u, signature, sigName = "signature";
string str = cipher;
str.replace("\\u0026", "&");
array<string> params = str.split("&");
for (int i = 0, len = params.size(); i < len; i++)
{
string param = params[i];
int k = param.find("=");
if (k > 0)
{
string paramHeader = param.Left(k);
string paramValue = param.substr(k + 1);
if (paramHeader == "url") u = HostUrlDecode(paramValue);
else if (paramHeader == "s") signature = HostUrlDecode(paramValue);
else if (paramHeader == "sp") sigName = paramValue;
else if (!u.empty()) u = u + "&" + paramHeader + "=" + HostUrlDecode(paramValue);
}
else if (!u.empty()) u = u + "&" + param;
}
if (!u.empty() && !signature.empty() && !jsData.empty())
{
string param = "&" + sigName + "=";
u = SignatureDecode(u, signature, param, webData, jsData, JSFuncs, JSFuncArgs);
}
u.replace("\\u0026", "&");
return u;
}
if (url.empty()) return "";
url.replace("\\u0026", "&");
if (url.find("/s/") > 0)
{
string tmp = url;
string signature = HostRegExpParse(tmp, "/s/([0-9A-Z]+.[0-9A-Z]+)");
if (!signature.empty()) url = SignatureDecode(tmp, signature, "/signature/", webData, jsData, JSFuncs, JSFuncArgs);
}
return url;
}
After this insert, string ReplaceCodecName(string name, string id) should still be the next function.
Edit 2 — add decoder state in PlayitemParse
FIND this (inside PlayitemParse, right after string tmp_fn = fn;):
string fn = path;
string tmp_fn = fn;
tmp_fn.MakeLower();
REPLACE with:
string fn = path;
string tmp_fn = fn;
array<youtubeFuncType> JSFuncs;
array<int> JSFuncArgs;
tmp_fn.MakeLower();
Edit 3 — replace the three HostDecodeSigUrl calls
Search for HostDecodeSigUrl. There are exactly three.
3a. Live dash/HLS (around original line 1561)
FIND:
liveUrl = HostDecodeSigUrl(liveUrl, "", JSData);
REPLACE with:
liveUrl = DecodeSigUrl(liveUrl, "", WebData, JSData, JSFuncs, JSFuncArgs);
3b. Adaptive formats (url or signatureCipher / cipher, around original line 1679)
FIND:
resolvedUrl = HostDecodeSigUrl(resolvedUrl, cipherText, JSData);
REPLACE with:
resolvedUrl = DecodeSigUrl(resolvedUrl, cipherText, WebData, JSData, JSFuncs, JSFuncArgs);
3c. Non-live dash/HLS fallback (around original line 1721)
FIND:
url = HostDecodeSigUrl(url, "", JSData);
REPLACE with:
url = DecodeSigUrl(url, "", WebData, JSData, JSFuncs, JSFuncArgs);
Check
- Search the file:
HostDecodeSigUrl should match 0 times.
- Search:
DecodeSigUrl should match 4 times (the function definition plus the three call sites).
- Restart PotPlayer and open a YouTube URL. The AngelScript console should no longer report
No matching symbol 'HostDecodeSigUrl'.
Result
After that the script compiles on hosts that do not export HostDecodeSigUrl, and signed stream URLs decode the same way the stock YouTube parser does.
Ok.... this is what I got when fixing it with cursor Appreciate the hard work always!
Fix for older PotPlayer:
HostDecodeSigUrlis missingDiffed against the original PotPlayer-SponsorBlock script (
MediaPlayParse - YouTube with SponsorBlock.as).That is the only functional change — 2,840 lines → 3,112 lines, five hunks, nothing else. SponsorBlock chapters, Innertube, quality list, etc. are untouched.
Problem
On older PotPlayer builds this script never actually runs. The AngelScript console shows:
HostDecodeSigUrlis a host API that exists only in newer PotPlayer. Without it the parser does not compile, so YouTube URLs fail / flicker through qualities and never play.The stock bundled
MediaPlayParse - YouTube.asalready has a local JS signature decoder (SignatureDecode+youtubeFuncType/Delete/Swap/Reverse/GetFunction). I copied that into this SponsorBlock fork and wrapped it asDecodeSigUrl(...)so the three existing call sites could stay the same shape.Changes
1. Inserted local decoder (before
ReplaceCodecName)Added:
youtubeFuncType,Delete,Swap,Reverse,GetFunction,SignatureDecode, andDecodeSigUrl.DecodeSigUrlis a drop-in forHostDecodeSigUrl(url, cipher, jsData): it parsessignatureCipher/cipher(url/s/sp), applies the player-JS transform, and also handles/s/in dash/HLS URLs.The full decoder block is in the working file at lines 1021–1289, copied from stock
MediaPlayParse - YouTube.as.2. In
PlayitemParse, declare the decoder state next to the other locals3. Replace the three
HostDecodeSigUrlcallsLive dash/HLS:
Adaptive formats (
urlorsignatureCipher/cipher):Non-live dash/HLS fallback:
How to patch your own copy
Open
MediaPlayParse - YouTube with SponsorBlock.asin a text editor. There are four edits: one insert, one local-variable add, and three one-line replacements. Search for theFINDtext; it should match once each time.When you are done, there must be zero remaining
HostDecodeSigUrlin the file. Copy the patched.asinto:PotPlayer\Extension\Media\PlayParse\Restart PotPlayer (or reload extensions).
Edit 1 — insert the local decoder
FIND this (it is the start of
ReplaceCodecName, around line 1021 in the original):INSERT the entire block below immediately before that line. Do not delete
ReplaceCodecName.After this insert,
string ReplaceCodecName(string name, string id)should still be the next function.Edit 2 — add decoder state in
PlayitemParseFIND this (inside
PlayitemParse, right afterstring tmp_fn = fn;):REPLACE with:
Edit 3 — replace the three
HostDecodeSigUrlcallsSearch for
HostDecodeSigUrl. There are exactly three.3a. Live dash/HLS (around original line 1561)
FIND:
REPLACE with:
3b. Adaptive formats (
urlorsignatureCipher/cipher, around original line 1679)FIND:
REPLACE with:
3c. Non-live dash/HLS fallback (around original line 1721)
FIND:
REPLACE with:
Check
HostDecodeSigUrlshould match 0 times.DecodeSigUrlshould match 4 times (the function definition plus the three call sites).No matching symbol 'HostDecodeSigUrl'.Result
After that the script compiles on hosts that do not export
HostDecodeSigUrl, and signed stream URLs decode the same way the stock YouTube parser does.