From 3787c02ce7f2167aeca8f6c32a48eeddf91e2034 Mon Sep 17 00:00:00 2001 From: 1fexd Date: Fri, 4 Oct 2024 20:54:10 +0200 Subject: [PATCH 1/2] fix(sync): Fix Reddit video downloader parsing --- .../FixRedditVideoDownloadPatch.java | 13 +++++ .../internal/RedditVideoPlaylistParser.kt | 51 +++++++++++++++++++ 2 files changed, 64 insertions(+) create mode 100644 app/src/main/java/app/revanced/integrations/syncforreddit/FixRedditVideoDownloadPatch.java create mode 100644 app/src/main/java/app/revanced/integrations/syncforreddit/internal/RedditVideoPlaylistParser.kt diff --git a/app/src/main/java/app/revanced/integrations/syncforreddit/FixRedditVideoDownloadPatch.java b/app/src/main/java/app/revanced/integrations/syncforreddit/FixRedditVideoDownloadPatch.java new file mode 100644 index 0000000000..1d3ab6ab3f --- /dev/null +++ b/app/src/main/java/app/revanced/integrations/syncforreddit/FixRedditVideoDownloadPatch.java @@ -0,0 +1,13 @@ +package app.revanced.integrations.syncforreddit; + +import app.revanced.integrations.syncforreddit.internal.RedditVideoPlaylistParser; + +/** + * @noinspection unused + */ +public class FixRedditVideoDownloadPatch { + + public static String[] getLinks(byte[] data) { + return RedditVideoPlaylistParser.INSTANCE.parse(data); + } +} diff --git a/app/src/main/java/app/revanced/integrations/syncforreddit/internal/RedditVideoPlaylistParser.kt b/app/src/main/java/app/revanced/integrations/syncforreddit/internal/RedditVideoPlaylistParser.kt new file mode 100644 index 0000000000..b2cdb9c4f3 --- /dev/null +++ b/app/src/main/java/app/revanced/integrations/syncforreddit/internal/RedditVideoPlaylistParser.kt @@ -0,0 +1,51 @@ +package app.revanced.integrations.syncforreddit.internal + +import org.w3c.dom.Element +import java.io.ByteArrayInputStream +import javax.xml.parsers.DocumentBuilderFactory + +object RedditVideoPlaylistParser { + class MpdEntry(val bandwidth: Int, val baseUrl: String) + + private fun getBestMpEntry(element: Element): MpdEntry? { + val representations = element.getElementsByTagName("Representation") + val entries = mutableListOf() + for (i in 0 until representations.length) { + val representation = representations.item(i) as Element + val bandwidth = representation.getAttribute("bandwidth")?.toIntOrNull() + val baseUrl = representation.getElementsByTagName("BaseURL").item(0) + + if (bandwidth != null && baseUrl != null) { + entries.add(MpdEntry(bandwidth, baseUrl.textContent)) + } + } + + return entries.maxByOrNull { it.bandwidth } + } + + fun parse(data: ByteArray): Array { + val adaptionSets = DocumentBuilderFactory + .newInstance() + .newDocumentBuilder() + .parse(ByteArrayInputStream(data)) + .getElementsByTagName("AdaptationSet") + + var videoUrl: String? = null + var audioUrl: String? = null + + for (i in 0 until adaptionSets.length) { + val element = adaptionSets.item(i) as Element + val contentType = element.getAttribute("contentType") + val bestEntry = getBestMpEntry(element) ?: continue + + when (contentType) { + "video" -> videoUrl = bestEntry.baseUrl + "audio" -> audioUrl = bestEntry.baseUrl + } + } + + return arrayOf(videoUrl, audioUrl) + } +} + + From 91b27695f3c668738102cf9036942fdf4bbd79f9 Mon Sep 17 00:00:00 2001 From: 1fexd Date: Fri, 4 Oct 2024 22:31:36 +0200 Subject: [PATCH 2/2] chore: Implement patch in Java --- .../FixRedditVideoDownloadPatch.java | 68 ++++++++++++++++++- .../internal/RedditVideoPlaylistParser.kt | 51 -------------- 2 files changed, 66 insertions(+), 53 deletions(-) delete mode 100644 app/src/main/java/app/revanced/integrations/syncforreddit/internal/RedditVideoPlaylistParser.kt diff --git a/app/src/main/java/app/revanced/integrations/syncforreddit/FixRedditVideoDownloadPatch.java b/app/src/main/java/app/revanced/integrations/syncforreddit/FixRedditVideoDownloadPatch.java index 1d3ab6ab3f..f1701ae9dd 100644 --- a/app/src/main/java/app/revanced/integrations/syncforreddit/FixRedditVideoDownloadPatch.java +++ b/app/src/main/java/app/revanced/integrations/syncforreddit/FixRedditVideoDownloadPatch.java @@ -1,13 +1,77 @@ package app.revanced.integrations.syncforreddit; -import app.revanced.integrations.syncforreddit.internal.RedditVideoPlaylistParser; +import android.util.Pair; +import androidx.annotation.Nullable; +import org.w3c.dom.Element; +import org.xml.sax.SAXException; + +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; /** * @noinspection unused */ public class FixRedditVideoDownloadPatch { + private static @Nullable Pair getBestMpEntry(Element element) { + var representations = element.getElementsByTagName("Representation"); + var entries = new ArrayList>(); + + for (int i = 0; i < representations.getLength(); i++) { + Element representation = (Element) representations.item(i); + var bandwidthStr = representation.getAttribute("bandwidth"); + try { + var bandwidth = Integer.parseInt(bandwidthStr); + var baseUrl = representation.getElementsByTagName("BaseURL").item(0); + if (baseUrl != null) { + entries.add(new Pair<>(bandwidth, baseUrl.getTextContent())); + } + } catch (NumberFormatException ignored) { + } + } + + if (entries.isEmpty()) { + return null; + } + + Collections.sort(entries, (e1, e2) -> e2.first - e1.first); + return entries.get(0); + } + + private static String[] parse(byte[] data) throws ParserConfigurationException, IOException, SAXException { + var adaptionSets = DocumentBuilderFactory + .newInstance() + .newDocumentBuilder() + .parse(new ByteArrayInputStream(data)) + .getElementsByTagName("AdaptationSet"); + + String videoUrl = null; + String audioUrl = null; + + for (int i = 0; i < adaptionSets.getLength(); i++) { + Element element = (Element) adaptionSets.item(i); + var contentType = element.getAttribute("contentType"); + var bestEntry = getBestMpEntry(element); + if (bestEntry == null) continue; + + if (contentType.equalsIgnoreCase("video")) { + videoUrl = bestEntry.second; + } else if (contentType.equalsIgnoreCase("audio")) { + audioUrl = bestEntry.second; + } + } + + return new String[]{videoUrl, audioUrl}; + } public static String[] getLinks(byte[] data) { - return RedditVideoPlaylistParser.INSTANCE.parse(data); + try { + return parse(data); + } catch (ParserConfigurationException | IOException | SAXException e) { + return new String[]{null, null}; + } } } diff --git a/app/src/main/java/app/revanced/integrations/syncforreddit/internal/RedditVideoPlaylistParser.kt b/app/src/main/java/app/revanced/integrations/syncforreddit/internal/RedditVideoPlaylistParser.kt deleted file mode 100644 index b2cdb9c4f3..0000000000 --- a/app/src/main/java/app/revanced/integrations/syncforreddit/internal/RedditVideoPlaylistParser.kt +++ /dev/null @@ -1,51 +0,0 @@ -package app.revanced.integrations.syncforreddit.internal - -import org.w3c.dom.Element -import java.io.ByteArrayInputStream -import javax.xml.parsers.DocumentBuilderFactory - -object RedditVideoPlaylistParser { - class MpdEntry(val bandwidth: Int, val baseUrl: String) - - private fun getBestMpEntry(element: Element): MpdEntry? { - val representations = element.getElementsByTagName("Representation") - val entries = mutableListOf() - for (i in 0 until representations.length) { - val representation = representations.item(i) as Element - val bandwidth = representation.getAttribute("bandwidth")?.toIntOrNull() - val baseUrl = representation.getElementsByTagName("BaseURL").item(0) - - if (bandwidth != null && baseUrl != null) { - entries.add(MpdEntry(bandwidth, baseUrl.textContent)) - } - } - - return entries.maxByOrNull { it.bandwidth } - } - - fun parse(data: ByteArray): Array { - val adaptionSets = DocumentBuilderFactory - .newInstance() - .newDocumentBuilder() - .parse(ByteArrayInputStream(data)) - .getElementsByTagName("AdaptationSet") - - var videoUrl: String? = null - var audioUrl: String? = null - - for (i in 0 until adaptionSets.length) { - val element = adaptionSets.item(i) as Element - val contentType = element.getAttribute("contentType") - val bestEntry = getBestMpEntry(element) ?: continue - - when (contentType) { - "video" -> videoUrl = bestEntry.baseUrl - "audio" -> audioUrl = bestEntry.baseUrl - } - } - - return arrayOf(videoUrl, audioUrl) - } -} - -