use crate::{KODIK_STATE, scraper::Response};
use base64::{Engine as _, engine::general_purpose};
use kodik_utils::Error;
const MIN_SHIFT: u8 = 0;
const MAX_SHIFT: u8 = 26;
pub fn decode_links(kodik_response: &mut Response) -> Result<(), Error> {
log::debug!("Decoding links...");
for link in &mut kodik_response.links.quality_360 {
link.src = decode_link(&link.src)?;
}
let base_360 = kodik_response.links.quality_360.first();
for link in &mut kodik_response.links.quality_480 {
link.src = match base_360 {
Some(link) => link.src.replace("/360.mp4", "/480.mp4"),
None => decode_link(&link.src)?,
};
}
for link in &mut kodik_response.links.quality_720 {
link.src = match base_360 {
Some(link) => link.src.replace("/360.mp4", "/720.mp4"),
None => decode_link(&link.src)?,
};
}
log::trace!("Decoded links: {:#?}", kodik_response.links);
Ok(())
}
pub fn decode_link(src: &str) -> Result<String, Error> {
let shift = KODIK_STATE.shift().clamp(MIN_SHIFT, MAX_SHIFT);
if let Ok(decoded) = try_decode(src, shift) {
return Ok(decoded);
}
for shift in MIN_SHIFT..=MAX_SHIFT {
if let Ok(decoded) = try_decode(src, shift) {
KODIK_STATE.set_shift(shift);
return Ok(decoded);
}
}
Err(Error::LinkCannotBeDecoded(src.to_owned()))
}
pub fn try_decode(encoded: &str, shift: u8) -> Result<String, Error> {
let mut decoded_caesar = caesar_cipher(encoded, shift);
while !decoded_caesar.len().is_multiple_of(4) {
decoded_caesar.push('=');
}
let decode_result = decode_base64(&decoded_caesar);
if let Ok(mut decoded) = decode_result {
if !decoded.starts_with("https:") {
decoded.insert_str(0, "https:");
}
return Ok(decoded);
}
decode_result
}
pub fn caesar_cipher(text: &str, shift: u8) -> String {
text.chars()
.map(|c| {
if c.is_ascii_alphabetic() {
let base = if c.is_ascii_lowercase() { b'a' } else { b'A' };
let pos = c as u8 - base;
let new_pos = (pos + MAX_SHIFT - shift) % MAX_SHIFT;
(base + new_pos) as char
} else {
c
}
})
.collect()
}
pub fn decode_base64(input: &str) -> Result<String, Error> {
let decoded_input = general_purpose::STANDARD.decode(input)?;
Ok(String::from_utf8(decoded_input)?)
}