Display transcript text and follow along the audio (#7103)

This commit is contained in:
Tony Tam
2024-05-18 09:54:14 -07:00
committed by ByteHamster
parent 7c4f19c979
commit e856a9f118
30 changed files with 800 additions and 129 deletions

View File

@ -13,9 +13,11 @@ dependencies {
implementation project(':net:common')
implementation project(':parser:media')
implementation project(':parser:feed')
implementation project(':parser:transcript')
implementation project(':storage:database')
annotationProcessor "androidx.annotation:annotation:$annotationVersion"
implementation "commons-io:commons-io:$commonsioVersion"
implementation "org.apache.commons:commons-lang3:$commonslangVersion"
implementation "com.squareup.okhttp3:okhttp:$okhttpVersion"
}

View File

@ -1,55 +0,0 @@
package de.danoeh.antennapod.ui.chapters;
import android.util.Log;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.charset.Charset;
import de.danoeh.antennapod.model.feed.FeedMedia;
import de.danoeh.antennapod.net.common.AntennapodHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import org.apache.commons.io.IOUtils;
public class PodcastIndexTranscriptUtils {
private static final String TAG = "PodcastIndexTranscript";
public static String loadTranscriptFromUrl(String type, String url, boolean forceRefresh) {
StringBuilder str = new StringBuilder();
Response response = null;
try {
Log.d(TAG, "Downloading transcript URL " + url.toString());
Request request = new Request.Builder().url(url).build();
response = AntennapodHttpClient.getHttpClient().newCall(request).execute();
if (response.isSuccessful() && response.body() != null) {
str.append(response.body().string());
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (response != null) {
response.close();
}
}
return str.toString();
}
public static void storeTranscript(FeedMedia media, String transcript) {
File transcriptFile = new File(media.getTranscriptFileUrl());
FileOutputStream ostream = null;
try {
if (!transcriptFile.exists() && transcriptFile.createNewFile()) {
ostream = new FileOutputStream(transcriptFile);
ostream.write(transcript.getBytes(Charset.forName("UTF-8")));
ostream.close();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
IOUtils.closeQuietly(ostream);
}
}
}

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960">
<path
android:fillColor="?attr/action_icon_color"
android:pathData="M240,640L560,640L560,560L240,560L240,640ZM640,640L720,640L720,560L640,560L640,640ZM240,480L320,480L320,400L240,400L240,480ZM400,480L720,480L720,400L400,400L400,480ZM160,800Q127,800 103.5,776.5Q80,753 80,720L80,240Q80,207 103.5,183.5Q127,160 160,160L800,160Q833,160 856.5,183.5Q880,207 880,240L880,720Q880,753 856.5,776.5Q833,800 800,800L160,800ZM160,720L800,720Q800,720 800,720Q800,720 800,720L800,240Q800,240 800,240Q800,240 800,240L160,240Q160,240 160,240Q160,240 160,240L160,720Q160,720 160,720Q160,720 160,720ZM160,720Q160,720 160,720Q160,720 160,720L160,240Q160,240 160,240Q160,240 160,240L160,240Q160,240 160,240Q160,240 160,240L160,720Q160,720 160,720Q160,720 160,720L160,720Z"/>
</vector>

View File

@ -258,6 +258,10 @@
<item quantity="other">%d episodes removed from inbox.</item>
</plurals>
<string name="add_to_favorite_label">Add to favorites</string>
<string name="show_transcript">Show transcript</string>
<string name="transcript">Transcript</string>
<string name="transcript_follow">Follow audio</string>
<string name="no_transcript_label">No transcript</string>
<string name="remove_from_favorite_label">Remove from favorites</string>
<string name="visit_website_label">Visit website</string>
<string name="skip_episode_label">Skip episode</string>

View File

@ -0,0 +1,20 @@
plugins {
id("com.android.library")
}
apply from: "../../common.gradle"
apply from: "../../playFlavor.gradle"
android {
namespace "de.danoeh.antennapod.ui.transcript"
}
dependencies {
implementation project(':model')
implementation project(':net:common')
implementation project(':parser:media')
implementation project(':parser:transcript')
implementation "commons-io:commons-io:$commonsioVersion"
implementation "org.apache.commons:commons-lang3:$commonslangVersion"
implementation "com.squareup.okhttp3:okhttp:$okhttpVersion"
}

View File

@ -0,0 +1,111 @@
package de.danoeh.antennapod.ui.transcript;
import android.util.Log;
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InterruptedIOException;
import java.nio.charset.Charset;
import de.danoeh.antennapod.model.feed.FeedMedia;
import de.danoeh.antennapod.net.common.AntennapodHttpClient;
import de.danoeh.antennapod.model.feed.Transcript;
import de.danoeh.antennapod.parser.transcript.TranscriptParser;
import okhttp3.CacheControl;
import okhttp3.Request;
import okhttp3.Response;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
public class TranscriptUtils {
private static final String TAG = "Transcript";
public static String loadTranscriptFromUrl(String url, boolean forceRefresh) throws InterruptedIOException {
if (forceRefresh) {
return loadTranscriptFromUrl(url, CacheControl.FORCE_NETWORK);
}
String str = loadTranscriptFromUrl(url, CacheControl.FORCE_CACHE);
if (str == null || str.length() <= 1) {
// Some publishers use one dummy transcript before actual transcript are available
return loadTranscriptFromUrl(url, CacheControl.FORCE_NETWORK);
}
return str;
}
private static String loadTranscriptFromUrl(String url, CacheControl cacheControl) throws InterruptedIOException {
StringBuilder str = new StringBuilder();
Response response = null;
try {
Log.d(TAG, "Downloading transcript URL " + url);
Request request = new Request.Builder().url(url).cacheControl(cacheControl).build();
response = AntennapodHttpClient.getHttpClient().newCall(request).execute();
if (response.isSuccessful() && response.body() != null) {
Log.d(TAG, "Done Downloading transcript URL " + url);
str.append(response.body().string());
} else {
Log.d(TAG, "Error Downloading transcript URL " + url + ": " + response.message());
}
} catch (InterruptedIOException e) {
Log.d(TAG, "InterruptedIOException while downloading transcript URL " + url);
throw e;
} catch (Exception e) {
e.printStackTrace();
return null;
} finally {
if (response != null) {
response.close();
}
}
return str.toString();
}
public static Transcript loadTranscript(FeedMedia media, Boolean forceRefresh) throws InterruptedIOException {
String transcriptType = media.getItem().getTranscriptType();
if (!forceRefresh && media.getItem().getTranscript() != null) {
return media.getTranscript();
}
if (!forceRefresh && media.getTranscriptFileUrl() != null) {
File transcriptFile = new File(media.getTranscriptFileUrl());
try {
if (transcriptFile.exists()) {
String t = FileUtils.readFileToString(transcriptFile, (String) null);
if (StringUtils.isNotEmpty(t)) {
media.setTranscript(TranscriptParser.parse(t, transcriptType));
return media.getTranscript();
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
String transcriptUrl = media.getItem().getTranscriptUrl();
String t = TranscriptUtils.loadTranscriptFromUrl(transcriptUrl, forceRefresh);
if (StringUtils.isNotEmpty(t)) {
return TranscriptParser.parse(t, transcriptType);
}
return null;
}
public static void storeTranscript(FeedMedia media, String transcript) {
File transcriptFile = new File(media.getTranscriptFileUrl());
FileOutputStream ostream = null;
try {
if (transcriptFile.exists() && !transcriptFile.delete()) {
Log.e(TAG, "Failed to delete existing transcript file " + transcriptFile.getAbsolutePath());
}
if (transcriptFile.createNewFile()) {
ostream = new FileOutputStream(transcriptFile);
ostream.write(transcript.getBytes(Charset.forName("UTF-8")));
ostream.close();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
IOUtils.closeQuietly(ostream);
}
}
}