Kodik internal ID support, URL decoding
This commit is contained in:
@ -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);
|
||||||
);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user