Closed awang-karisma closed 6 years ago
Hi this is most likely happening because 9anime added a server query param for the url used to get videos
I will try to find a fix for this asap.
@lap00zza the problem is more deeper on that
they have encode the token for the grabber url encoded using client side javascript from /all.js
$.ajax(e.grabber, { data: o, success: function(t) { i[R](uU, e.subtitle), t[Xy] ? t[Xy] === di ? r.Ei(xM + sp + di + sp + kE + sp + vP + sp + kH + sp + ST + sp + Qa + sp + jM + sp + fF + sp + n) : (r.$i(i, t[Xy], t.token), r.Fi(i, t[Xy])) : r.Xi(i, t[R]) }, error: function() { iR < 1 ? (i[R](dt, iR + 1), r.Oi(i, e)) : r.Ei(xM + sp + iz + sp + GU + sp + vP + sp + fF + sp + n + Ey + gr) } }) },
@grabberboy yup. I was just looking at it via the chrome debugger sometime back. Thing is this decryption happens sometime between onsuccess/oncomplete and before generating the _
. But the thing is if I manually step in and trace line by line the decryption function never seems to get called (weird), My best guess is that they put a time duration check during which it can be decrypted. (manually tracing line by line gets countered by this check 😢 )
@lap00zza the time duration is the worst scenario lets look more on it
My findings till now. I got really tired after being at this for 4 hours. Might take a crack at it again tomorrow. Some of the variables are renamed. If anybody wants to continue cracking this, please feel free.
// Tracehelper
window.src = 12
window.fakeSrc = "";
Object.defineProperty(window, "src", {
configurable: false,
get() {
return fakeSrc;
},
set(x) {
console.trace();
console.log(JSON.parse(JSON.stringify(x)));
this.fakeSrc = x;
}
});
/* --- */
// so
// -> ajax call to get link
// -> `success` calls `NI` with the JSON response and the callback `cb`
// -> `NI` calls callback `cb` after 200 ms
// -> `cb` calls `OI` with the epsiode element and `x` (contents of window.src)
// `x` contains the decoded token and option parameter
// Final observation, js has ~200 ms to decrypt token/option param. IF it gets
// delayed Oi gets called with encrypted token/option.
// ---
// t is the episode element
function success(n) {
Ni(n, function(x) /*name: cb*/ {
if(x.error)
i.Ei("An error occured please refresh the page (ERR 1)")
else
if (x.type === "direct")
Oi(t, x)
else
if (x.type === "iframe")
i.Ri(x.target)
})
}
function Ni(i, n) {
var e = this;
window.src = i,
window.srcsrc = !0, /* !0 = true */
window.setTimeout(function() {
i = window.src,
delete window.src,
delete window.srcsrc,
n.call(e, i)
}, 200)
}
// i is the element
// e is the json object
function Oi(i, e) {
var r = this
, o = e.params;
return i.data("apiErrorCount", i.data("apiErrorCount") || 0),
$[oI](o, {
_i: t.XTOKEN,
mobile: n.mobile() ? 1 : 0
}),
$.ajax(e.grabber, {
data: o,
success: function(t) {
i.data("subtitle", e.subtitle),
if(t.error)
if (t.error === "token")
r.Ei("API: token invalid please refresh this page and try again")
else
(r.$i(i, t[Xy], t.token), r.Fi(i, t[Xy]))
else
r.Xi(i, t[R])
},
error: function() {/*...*/}
});
}
// second function responsible for decryption
window.setTimeout(function() {
if(window.src)
if(window.srcsrc === true)
window.srcsrc = false;
window.src = decrypt(window.src)
}, 50);
function decrypt(t) {
var n;
if (Object.prototype.toString.call(t) === "[object Array]")
for (n = 0; n < t.length; n++)
t[n] = decrypt(t[n]);
else if (Object.prototype.toString.call(t) === "[object Object]")
for (n in t)
if(Object.prototype.hasOwnProperty.call(t, n))
t[n] = decrypt(t[n]);
else
typeof t === "string" && (t = t[0] === "." ? function(t, i) {
var n, e = [];
if (i || (i = 13),
!(i %= 26))
return t;
for (n = 0,
l = t.length; n < l; n++)
c = t.charCodeAt(n),
c >= 97 && c <= 122 ? e[n] = (c - 71 + i) % 26 + 97 : c >= 65 && c <= 90 ? e[n] = (c - 39 + i) % 26 + 65 : e[n] = c;
return String.fromCharCode.apply(vA, e)
}(t.substr(1), -18) : t);
return t
}
/* --- */
// notes
// -> _ is calculated from the encrypted token and options
@lap00zza the encryption is rot-8. you have to decrypt all variable values starting with a dot.
for example: in params.token the value starts with a . and options starts with a . (dot) too. if its not a direct link but an iframe, youll see the target starts with a . and has to be decrypted too. thats it, nothing more
here in python, this method strips the dot by itself in "range(1, len(str))"
def rot8(str):
i = -18 # line: 6850
e = []
for q in range(1, len(str)):
intChar = ord(str[q])
newChar = 0
if 97 <= intChar <= 122:
newChar = (intChar - 71 + i) % 26 + 97
elif 65 <= intChar <= 90:
newChar = (intChar - 39 + i) % 26 + 65
else:
newChar = intChar
e.append(newChar)
charArray = [chr(c) for c in e]
return ''.join(charArray) #str(bytearray(e))
@hoppler WOAH thanks so much. I was having trouble identifying. Just tested, it is indeed rot-8.
Not just failing, it doesn't even showed up like "Downloading 001" (select all or just one episode) then all "Done".
good findings but still the ts value is encrypted in unknown method
hope @hoppler can see this too
@Vysair the fix will take some time. There were 2 main hindrances:
ts
Once the 2nd one is done I will release a fix for the downloads. (Best guess: within max 2 days)
@lap00zza Here you go (happens all in line 12320 all.js)
def main():
expected = "1511690400"
ts = "YNPxYNX5YGHwYA=="
firstCharMap = []
secondCharMap = []
for n in range(65, 91):
firstCharMap.append(chr(n))
if n % 2 != 0:
secondCharMap.append(chr(n))
for n in range(65, 91):
if n % 2 == 0:
secondCharMap.append(chr(n))
result = ""
for i in range(len(ts)):
charReplaced = False
for y in range(len(secondCharMap)):
if ts[i] == secondCharMap[y]:
result += firstCharMap[y]
charReplaced = True
break
if not charReplaced:
result += ts[i]
import base64
real_ts = base64.b64decode(result)
print real_ts
print "Success" if real_ts == expected else "failure"
if __name__ == '__main__':
main()
Thanks so much @hoppler . For anyone reading this thread, I will release a fix for 9anime Companion tomorrow.
Can someone please test these artifacts: https://ci.appveyor.com/project/lap00zza/9anime-companion/build/1.4.0.24/artifacts and tell me if downloads are working for F2, G2 and G3. Install instructions can be found here: https://github.com/lap00zza/9anime-Companion/wiki/Running-in-Developement-Mode
Can't wait for the fix ! unfortunately can't help with testing because I'm not at home, but hopefully the issue will be resolved till tomorrow !
Getting this after installing. And download from F2, G2 and G3 still not working.
they update again
token is not in rot8 anymore
Maybe they are spying on us? I wish i could help but i have been spending too much time watching anime and too little working on my JS skills (i fell bad for it).
Anyways, thanks to everyone working on solving this issue and i hope that i can help in future ones.
https://twitter.com/9animeto/status/933685120539561984
They have said it
Looking at their discord it doesn't seem like they are going to war anyone.
We can only hope that this is only one-time change, and things get stabilize soon.
Well, that discord message dates september and the tweet is from november so it is a little outdated.
Probably because of a misunderstanding. One of my friend who knows the owner told me the owner thinks:
when they enabled the adblock detection I made 9anime Companion not work on purpose
But that is not at all true. It was still working if you disabled Remove Ads and Remove Social Share. But I have no clue why they added detection for social share. Ads I can understand.
Well, i only started to use 9anime a few days ago, it said "we stopped the pop up's please turn off the adblocker" and so did but now they are using pop ups again, there is no option to donate or pay to turn off this ads. I believe that a better service will make more donators and more people willing to help. A good way to make the service better is to coexist with this extension that makes the website much better.
Well i dont know if this will help us, but i think you should remove the ability to hide the ads, maybe by doing that, we are showing to 9anime guys that we dont have intention to hurt their income
leave adblocking to the users, they can use adblock extensions
I think @awangn6600 is right. You should probably remove the adblock feature. Also, it would be better if you could talk to the admin of 9anime and figure something out. I assure you people would love to have 9anime companion as an official extension for 9anime.
A fix is on the way but if possible can you guys please share your thought about remove adblocker here: https://www.reddit.com/r/9anime/comments/7ftuvp/poll_9anime_companion_remove_ads/
tentative fix for downloads: https://ci.appveyor.com/project/lap00zza/9anime-companion/build/1.4.0.25/artifacts
I choose this
Remove Remove Ads from the official build (Chrome Web Store, Addons.Mozilla.Org), but still be available in a separate branch in the GitHub repository so that people can compile themselves.
The tentative fix is working.
Great work, thank you.
@lap00zza rapidvideo removed? i always use rapid video because the download link doesnt expire quickly like google server one
is there any problem with rapidvideo?
@awangn6600 its target link is encrypted as well. I will patch it as soon as I find out how to decrypt.
could you take give me a lead which part of the code to take a look? i will try to figure out, although it might not give any result lol
@awangn6600 sure. Basically you need to look in a file called all.js
. But its encrypted (packed js). So you will need to wait till its decrypted and then check the called lines from the f12 Network tab. I am really not good at explaining this part 😠. If you are trying remember to un-comment this line:
@awangn6600 lets continue the rapidvideo discussion here: #109
Select your issue type: (check at least one)
Describe your issue: Is there any change in 9anime side? because when i tried to download any anime from any server, it always gives me Failed message i dont know how to get the error message, tried installing in developer mode and checked console log, nothing appears there