feat(YouTube - Overlay buttons): Improve the translation workflow of subtitles after Yandex transcription

This commit is contained in:
Aaron Veil 2026-01-26 11:20:40 +03:00
parent 77c52ee38c
commit 88e54e64db
3 changed files with 87 additions and 2 deletions

View file

@ -374,8 +374,10 @@ public final class GeminiManager {
}
// Call Gemini for translation
String cleanedJson = YandexVotUtils.stripTokensFromYandexJson(rawIntermediateJson);
GeminiUtils.translateYandexJson(
rawIntermediateJson,
cleanedJson,
targetLangName,
apiKey,
new GeminiUtils.Callback() {

View file

@ -236,7 +236,19 @@ public class GeminiUtils {
.getJSONArray("parts")
.getJSONObject(0)
.getString("text");
final String finalResult = resultText.trim();
final String finalResult = sanitizeJsonOutput(resultText);
Logger.printDebug(() -> "Gemini RAW result (Sanitized): " + finalResult.substring(0, Math.min(finalResult.length(), 300)) + "...");
if (videoUrl == null) {
boolean looksLikeJson = finalResult.startsWith("[") || finalResult.startsWith("{");
if (!looksLikeJson) {
Logger.printInfo(() -> "Gemini JSON translation result doesn't look like valid JSON!");
Logger.printInfo(() -> "Start of content: " + finalResult.substring(0, Math.min(finalResult.length(), 50)));
mainThreadHandler.post(() -> callback.onFailure("Translation result format error. Expected JSON."));
return;
}
}
Logger.printDebug(() -> "Gemini RAW result received: " + finalResult.substring(0, Math.min(finalResult.length(), 300)) + "...");
@ -498,6 +510,22 @@ public class GeminiUtils {
return "English";
}
/**
* Cleans the Gemini response to remove potential Markdown formatting.
*/
private static String sanitizeJsonOutput(String text) {
if (text == null) return "";
text = text.trim();
if (text.startsWith("```")) {
text = text.replaceFirst("^```[a-zA-Z]*\\s*", "");
}
if (text.endsWith("```")) {
text = text.replaceAll("\\s*```$", "");
}
return text.trim();
}
/**
* Callback interface for Gemini API operations (summary, transcription).
* Defines methods to handle successful results or failures.

View file

@ -8,6 +8,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import app.revanced.extension.shared.utils.Logger;
import okhttp3.*;
import org.jetbrains.annotations.NotNull;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
@ -973,6 +974,60 @@ public class YandexVotUtils {
return serverMessage;
}
/**
* Helper method to strip the "tokens" array from Yandex JSON.
* This drastically reduces the size of the JSON sent to Gemini,
* preventing output truncation and saving tokens.
*
* @param jsonContent The raw JSON from Yandex.
* @return The JSON string with 'tokens' arrays removed from each subtitle entry.
*/
public static String stripTokensFromYandexJson(String jsonContent) {
if (TextUtils.isEmpty(jsonContent)) return jsonContent;
try {
// Handle case where JSON is just an array (non-standard Yandex output)
if (jsonContent.trim().startsWith("[")) {
JSONArray root = new JSONArray(jsonContent);
for (int i = 0; i < root.length(); i++) {
JSONObject item = root.getJSONObject(i);
if (item.has("tokens")) item.remove("tokens");
}
return root.toString();
} else {
// Handle standard object with "subtitles" array (standard Yandex output)
JSONObject root = getJsonObject(jsonContent);
return root.toString();
}
} catch (JSONException e) {
Logger.printException(() -> "Failed to strip tokens from JSON", e);
return jsonContent; // Return original if modification fails
}
}
/**
* Parses the provided JSON string into a JSONObject and removes the verbose "tokens"
* arrays from within the "subtitles" list. It also updates metadata flags to reflect
* the removal of tokens.
*
* @param jsonContent The raw JSON string containing the subtitle object.
* @return The modified {@link JSONObject} with tokens removed.
* @throws JSONException If the string cannot be parsed or the structure is invalid.
*/
@NotNull
private static JSONObject getJsonObject(String jsonContent) throws JSONException {
JSONObject root = new JSONObject(jsonContent);
if (root.has("subtitles")) {
JSONArray subs = root.getJSONArray("subtitles");
for (int i = 0; i < subs.length(); i++) {
JSONObject item = subs.getJSONObject(i);
if (item.has("tokens")) item.remove("tokens");
}
}
// Also remove top-level containsTokens flag if present
if (root.has("containsTokens")) root.put("containsTokens", false);
return root;
}
// endregion Utils
// region Interfaces and Inner Classes