Kodik internal ID support, URL decoding

This commit is contained in:
2025-06-01 01:06:37 +05:00
parent b47c190fa7
commit 637702dccf
2 changed files with 96 additions and 4 deletions

View File

@ -13,11 +13,15 @@ public interface KodikPlayerAPI {
} }
default String kinopoiskURL(String id) { default String kinopoiskURL(String id) {
return BASE_PLAYER_URL + "kinopoiskID%3D"; return BASE_PLAYER_URL + "kinopoiskID%3D" + id;
} }
default String imdbURL(String id) { default String imdbURL(String id) {
return BASE_PLAYER_URL + "kinopoiskID%3D"; return BASE_PLAYER_URL + "imdbID%3D" + id;
}
default String kodikIDURL(String id) {
return BASE_PLAYER_URL + "ID%3D" + id;
} }
@GET("get-player") @GET("get-player")
@ -26,9 +30,9 @@ public interface KodikPlayerAPI {
@Query("hasPlayer") Boolean hasPlayer, @Query("hasPlayer") Boolean hasPlayer,
@Query("url") String url, @Query("url") String url,
@Query("token") String token, @Query("token") String token,
@Query("ID") String kodikId,
@Query("shikimoriID") String shikimoriID, @Query("shikimoriID") String shikimoriID,
@Query("kinopoiskID") String kinopoiskID, @Query("kinopoiskID") String kinopoiskID,
@Query("imdbID") String imdbID @Query("imdbID") String imdbID);
);
} }

View File

@ -0,0 +1,88 @@
package com.backend.extractor.kodik.service.anyame_backend.service;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import org.springframework.stereotype.Service;
/**
* KodikURLDecoder
*/
@Service
// TODO: Test shikimori id: 1535 with KodikURLDecoder, only mp4:hls:manifest is
// included
// TODO: Paste some random anime urls to test decoder
public class KodikURLDecoderService {
private static final String ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
private static final String MANIFEST_IDENTIFIER = "mp4:hls:manifest.m3u8";
private static final int BASE64_BLOCK_SIZE = 4;
/**
* Convert a character using ROT cipher with specified rotation
*/
private String convertChar(char ch, int rotation) {
boolean isLower = Character.isLowerCase(ch);
char upperChar = Character.toUpperCase(ch);
if (ALPHABET.indexOf(upperChar) != -1) {
int index = (ALPHABET.indexOf(upperChar) + rotation) % ALPHABET.length();
char result = ALPHABET.charAt(index);
return isLower ? String.valueOf(Character.toLowerCase(result)) : String.valueOf(result);
}
return String.valueOf(ch);
}
/**
* Try to decrypt the input string by testing all possible ROT cipher rotations
*/
private String tryToDecrypt(String input) {
for (int rotation = 0; rotation < 26; rotation++) {
StringBuilder rotatedString = new StringBuilder();
for (char ch : input.toCharArray()) {
rotatedString.append(convertChar(ch, rotation));
}
// Base64 strings must be multiples of 4 characters, add padding if needed
int missingPaddingLength = (BASE64_BLOCK_SIZE - (rotatedString.length() % BASE64_BLOCK_SIZE))
% BASE64_BLOCK_SIZE;
rotatedString.append("=".repeat(missingPaddingLength));
try {
byte[] decodedBytes = Base64.getDecoder().decode(rotatedString.toString());
String decodedResult = new String(decodedBytes, StandardCharsets.UTF_8);
if (decodedResult.contains(MANIFEST_IDENTIFIER)) {
return decodedResult;
}
} catch (IllegalArgumentException e) {
continue;
}
}
throw new KodikDecryptionException("Unable to decrypt the provided URL - no valid rotation found");
}
/**
* Public method to get link data from encoded video URL
*
* @param videoUrl The encoded video URL
* @return The decoded video URL
* @throws KodikDecryptionException if decryption fails
*/
public String getLinkData(String videoUrl) {
if (videoUrl == null || videoUrl.trim().isEmpty()) {
throw new IllegalArgumentException("Video URL cannot be null or empty");
}
return tryToDecrypt(videoUrl);
}
/**
* Custom exception for Kodik decryption failures
*/
public static class KodikDecryptionException extends RuntimeException {
public KodikDecryptionException(String message) {
super(message);
}
}
}