Skip to content

Commit 1999294

Browse files
authored
feat(YouTube - Voice Over Translation): Add authorization support for requesting live voice translation (#1555)
1 parent 1a18557 commit 1999294

14 files changed

Lines changed: 1352 additions & 220 deletions

File tree

extensions/shared/src/main/java/app/morphe/extension/youtube/patches/voiceovertranslation/VoiceOverTranslationPatch.java

Lines changed: 52 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -73,15 +73,8 @@ public class VoiceOverTranslationPatch {
7373

7474
private static final String TAG = "VOT";
7575

76-
private static final int STATUS_FAILED = 0;
77-
private static final int STATUS_FINISHED = 1;
78-
private static final int STATUS_WAITING = 2;
79-
private static final int STATUS_LONG_WAITING = 3;
80-
private static final int STATUS_PART_CONTENT = 5;
81-
private static final int STATUS_AUDIO_REQUESTED = 6;
8276
private static final long PAUSE_DETECTION_TIMEOUT_MS = 1500;
8377
private static final long PROXY_PREPARE_TIMEOUT_MS = 15000;
84-
private static final int AUDIO_REQUESTED_RETRY_DELAY_SECONDS = 3;
8578
private static final String PROXY_USER_AGENT =
8679
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 " +
8780
"(KHTML, like Gecko) Chrome/145.0.0.0 Safari/537.36";
@@ -371,8 +364,8 @@ private static void requestTranslation(
371364
return;
372365
}
373366
switch (result.status()) {
374-
case STATUS_FINISHED:
375-
case STATUS_PART_CONTENT:
367+
case VotApiClient.STATUS_FINISHED:
368+
case VotApiClient.STATUS_PART_CONTENT:
376369
if (result.audioUrl() != null && !result.audioUrl().isEmpty()) {
377370
String directUrl = result.audioUrl();
378371
String url = directUrl;
@@ -388,20 +381,23 @@ private static void requestTranslation(
388381
Utils.runOnMainThread(() -> showToastShort(str("revanced_vot_playback_error")));
389382
}
390383
break;
391-
case STATUS_WAITING:
392-
case STATUS_LONG_WAITING:
384+
case VotApiClient.STATUS_WAITING:
385+
case VotApiClient.STATUS_LONG_WAITING:
393386
int waitTime = result.remainingTime() > 0 ? result.remainingTime() : 5;
394387
setTranslationRequestWaiting(waitTime);
395388
Utils.runOnMainThread(() -> showToastShort(str("revanced_vot_stream_waiting", formatRemainingTime(waitTime))));
396389
pollTranslation(videoId, videoTitle, youtubeUrl, durationSeconds, sourceLang, targetLang, waitTime);
397390
break;
398-
case STATUS_AUDIO_REQUESTED:
391+
case VotApiClient.STATUS_AUDIO_REQUESTED:
399392
int audioWaitTime = result.remainingTime() > 0 ? result.remainingTime() : 10;
400393
setTranslationRequestWaiting(audioWaitTime);
401394
Utils.runOnMainThread(() -> showToastShort(str("revanced_vot_stream_waiting", formatRemainingTime(audioWaitTime))));
402395
handleAudioRequested(videoId, youtubeUrl, result.translationId(), durationSeconds, sourceLang, targetLang, videoTitle, audioWaitTime);
403396
break;
404-
case STATUS_FAILED:
397+
case VotApiClient.STATUS_SESSION_REQUIRED:
398+
Utils.runOnMainThread(() -> showToastShort(str("revanced_vot_auth_required")));
399+
break;
400+
case VotApiClient.STATUS_FAILED:
405401
default:
406402
if (Settings.VOT_USE_LIVE_VOICES.get()) {
407403
Settings.VOT_USE_LIVE_VOICES.save(false);
@@ -427,23 +423,18 @@ private static void pollTranslation(
427423
String sourceLang, String targetLang,
428424
int waitSeconds
429425
) {
430-
int maxRetries = 30;
431-
for (int retryCount = 0; retryCount < maxRetries; retryCount++) {
432-
try {
433-
Thread.sleep(waitSeconds * 1000L);
434-
} catch (InterruptedException e) {
435-
Thread.currentThread().interrupt();
436-
return;
437-
}
438-
String currentVideoId = VideoInformation.getVideoId();
439-
if (!videoId.equals(currentVideoId)) return;
440-
try {
441-
VotApiClient.TranslationResult result = VotApiClient.requestTranslation(
442-
url, duration, sourceLang, targetLang, videoTitle);
443-
if (result == null) continue;
444-
if (result.status() == STATUS_FINISHED || result.status() == STATUS_PART_CONTENT) {
445-
if (result.audioUrl() != null && !result.audioUrl().isEmpty()) {
446-
String directUrl = result.audioUrl();
426+
VotApiClient.TranslationResult result = VotApiClient.pollUntilReady(
427+
url, duration, sourceLang, targetLang, videoTitle,
428+
waitSeconds,
429+
new VotApiClient.PollHandler() {
430+
@Override
431+
public boolean isCancelled() {
432+
return !videoId.equals(VideoInformation.getVideoId());
433+
}
434+
435+
@Override
436+
public void onAudioReady(VotApiClient.TranslationResult res) {
437+
String directUrl = res.audioUrl();
447438
String audioUrl = directUrl;
448439
String fallback = null;
449440
if (Settings.VOT_AUDIO_PROXY_ENABLED.get()) {
@@ -453,31 +444,38 @@ private static void pollTranslation(
453444
final String audioUrlFinal = audioUrl;
454445
final String fallbackFinal = fallback;
455446
Utils.runOnMainThread(() -> startAudioPlayback(videoId, audioUrlFinal, fallbackFinal));
456-
return;
457447
}
458-
Utils.runOnMainThread(() -> showToastShort(str("revanced_vot_playback_error")));
459-
return;
460-
} else if (result.status() == STATUS_FAILED) {
461-
if (Settings.VOT_USE_LIVE_VOICES.get()) {
462-
Settings.VOT_USE_LIVE_VOICES.save(false);
463-
Utils.runOnMainThread(() -> showToastShort(str("revanced_vot_live_voices_unavailable")));
464-
requestTranslation(videoId, videoTitle, sourceLang, targetLang, duration);
465-
return;
448+
449+
@Override
450+
public void onAudioRequested(String videoUrl, String translationId) {
451+
sendAudioRequestedAudio(videoId, videoUrl, translationId);
452+
}
453+
454+
@Override
455+
public boolean onFailed() {
456+
if (Settings.VOT_USE_LIVE_VOICES.get()) {
457+
Settings.VOT_USE_LIVE_VOICES.save(false);
458+
Utils.runOnMainThread(() -> showToastShort(str("revanced_vot_live_voices_unavailable")));
459+
return true;
460+
}
461+
Utils.runOnMainThread(() -> showToastShort(str("revanced_vot_playback_error")));
462+
return false;
463+
}
464+
465+
@Override
466+
public void onSessionRequired() {
467+
Utils.runOnMainThread(() -> showToastShort(str("revanced_vot_auth_required")));
468+
}
469+
470+
@Override
471+
public void onWaiting(int wait, boolean isFirstWait) {
472+
setTranslationRequestWaiting(wait);
466473
}
467-
Utils.runOnMainThread(() -> showToastShort(str("revanced_vot_playback_error")));
468-
return;
469-
}
470-
waitSeconds = result.remainingTime() > 0 ? result.remainingTime() : 5;
471-
setTranslationRequestWaiting(waitSeconds);
472-
if (result.status() == STATUS_AUDIO_REQUESTED) {
473-
sendAudioRequestedAudio(videoId, url, result.translationId());
474-
waitSeconds = Math.min(waitSeconds, AUDIO_REQUESTED_RETRY_DELAY_SECONDS);
475474
}
476-
} catch (Exception e) {
477-
Logger.printException(() -> "pollTranslation failure", e);
478-
}
475+
);
476+
if (result == null) {
477+
Utils.runOnMainThread(() -> showToastShort(str("revanced_vot_stream_not_ready")));
479478
}
480-
Utils.runOnMainThread(() -> showToastShort(str("revanced_vot_stream_not_ready")));
481479
}
482480

483481
private static void handleAudioRequested(
@@ -487,8 +485,7 @@ private static void handleAudioRequested(
487485
) {
488486
try {
489487
sendAudioRequestedAudio(videoId, url, translationId);
490-
int retryDelaySeconds = Math.min(waitSeconds, AUDIO_REQUESTED_RETRY_DELAY_SECONDS);
491-
pollTranslation(videoId, videoTitle, url, duration, sourceLang, targetLang, retryDelaySeconds);
488+
pollTranslation(videoId, videoTitle, url, duration, sourceLang, targetLang, waitSeconds);
492489
} catch (Exception e) {
493490
Logger.printException(() -> "handleAudioRequested failed", e);
494491
Utils.runOnMainThread(() -> showToastShort(str("revanced_vot_playback_error")));
@@ -516,7 +513,8 @@ private static void sendAudioRequestedFallback(String url, String translationId)
516513
if (translationId != null && !translationId.isEmpty()) {
517514
String fallbackKey = url + "#" + translationId;
518515
if (!fallbackKey.equals(lastEmptyAudioFallbackKey)) {
519-
VotApiClient.sendEmptyAudio(url, translationId);
516+
VotApiClient.sendEmptyAudio(url, translationId,
517+
Settings.VOT_USE_LIVE_VOICES.get() ? Settings.VOT_OAUTH_TOKEN.get() : null);
520518
lastEmptyAudioFallbackKey = fallbackKey;
521519
}
522520
}

0 commit comments

Comments
 (0)