From c376be44dd14132dafe49e311714b637e3af61fd Mon Sep 17 00:00:00 2001 From: ByteHamster Date: Tue, 25 Jan 2022 21:55:38 +0100 Subject: [PATCH 1/2] When mime type is octet-stream, guess from the file name --- .../parser/feed/namespace/Atom.java | 9 +++----- .../parser/feed/namespace/Media.java | 22 ++++++++----------- .../parser/feed/namespace/Rss20.java | 11 +++------- .../parser/feed/util/SyndTypeUtils.java | 21 +++++++++++++++--- 4 files changed, 33 insertions(+), 30 deletions(-) diff --git a/parser/feed/src/main/java/de/danoeh/antennapod/parser/feed/namespace/Atom.java b/parser/feed/src/main/java/de/danoeh/antennapod/parser/feed/namespace/Atom.java index ef802c355..7e2f00607 100644 --- a/parser/feed/src/main/java/de/danoeh/antennapod/parser/feed/namespace/Atom.java +++ b/parser/feed/src/main/java/de/danoeh/antennapod/parser/feed/namespace/Atom.java @@ -92,14 +92,11 @@ public class Atom extends Namespace { Log.d(TAG, "Length attribute could not be parsed."); } String type = attributes.getValue(LINK_TYPE); - - if (type == null) { - type = SyndTypeUtils.getMimeTypeFromUrl(href); - } + String mimeType = SyndTypeUtils.getMimeType(type, href); FeedItem currItem = state.getCurrentItem(); - if (SyndTypeUtils.enclosureTypeValid(type) && currItem != null && !currItem.hasMedia()) { - currItem.setMedia(new FeedMedia(currItem, href, size, type)); + if (SyndTypeUtils.isMediaFile(mimeType) && currItem != null && !currItem.hasMedia()) { + currItem.setMedia(new FeedMedia(currItem, href, size, mimeType)); } } else if (LINK_REL_PAYMENT.equals(rel)) { state.getCurrentItem().setPaymentLink(href); diff --git a/parser/feed/src/main/java/de/danoeh/antennapod/parser/feed/namespace/Media.java b/parser/feed/src/main/java/de/danoeh/antennapod/parser/feed/namespace/Media.java index f480a0417..29d7d7999 100644 --- a/parser/feed/src/main/java/de/danoeh/antennapod/parser/feed/namespace/Media.java +++ b/parser/feed/src/main/java/de/danoeh/antennapod/parser/feed/namespace/Media.java @@ -49,27 +49,23 @@ public class Media extends Namespace { boolean validTypeMedia = false; boolean validTypeImage = false; boolean isDefault = "true".equals(defaultStr); - String guessedType = SyndTypeUtils.getMimeTypeFromUrl(url); + String mimeType = SyndTypeUtils.getMimeType(type, url); if (MEDIUM_AUDIO.equals(medium)) { validTypeMedia = true; - type = "audio/*"; + mimeType = "audio/*"; } else if (MEDIUM_VIDEO.equals(medium)) { validTypeMedia = true; - type = "video/*"; - } else if (MEDIUM_IMAGE.equals(medium) && (guessedType == null - || (!guessedType.startsWith("audio/") && !guessedType.startsWith("video/")))) { + mimeType = "video/*"; + } else if (MEDIUM_IMAGE.equals(medium) && (mimeType == null + || (!mimeType.startsWith("audio/") && !mimeType.startsWith("video/")))) { // Apparently, some publishers explicitly specify the audio file as an image validTypeImage = true; - type = "image/*"; + mimeType = "image/*"; } else { - if (type == null) { - type = guessedType; - } - - if (SyndTypeUtils.enclosureTypeValid(type)) { + if (SyndTypeUtils.isMediaFile(mimeType)) { validTypeMedia = true; - } else if (SyndTypeUtils.imageTypeValid(type)) { + } else if (SyndTypeUtils.isImageFile(mimeType)) { validTypeImage = true; } } @@ -94,7 +90,7 @@ public class Media extends Namespace { Log.e(TAG, "Duration \"" + durationStr + "\" could not be parsed"); } } - FeedMedia media = new FeedMedia(state.getCurrentItem(), url, size, type); + FeedMedia media = new FeedMedia(state.getCurrentItem(), url, size, mimeType); if (durationMs > 0) { media.setDuration(durationMs); } diff --git a/parser/feed/src/main/java/de/danoeh/antennapod/parser/feed/namespace/Rss20.java b/parser/feed/src/main/java/de/danoeh/antennapod/parser/feed/namespace/Rss20.java index a39e1b5b7..ad5eb9de8 100644 --- a/parser/feed/src/main/java/de/danoeh/antennapod/parser/feed/namespace/Rss20.java +++ b/parser/feed/src/main/java/de/danoeh/antennapod/parser/feed/namespace/Rss20.java @@ -48,16 +48,11 @@ public class Rss20 extends Namespace { } else if (ENCLOSURE.equals(localName) && ITEM.equals(state.getTagstack().peek().getName())) { String type = attributes.getValue(ENC_TYPE); String url = attributes.getValue(ENC_URL); - - boolean validType = SyndTypeUtils.enclosureTypeValid(type); - if (!validType) { - type = SyndTypeUtils.getMimeTypeFromUrl(url); - validType = SyndTypeUtils.enclosureTypeValid(type); - } + String mimeType = SyndTypeUtils.getMimeType(type, url); boolean validUrl = !TextUtils.isEmpty(url); if (state.getCurrentItem() != null && state.getCurrentItem().getMedia() == null - && validType && validUrl) { + && SyndTypeUtils.isMediaFile(mimeType) && validUrl) { long size = 0; try { size = Long.parseLong(attributes.getValue(ENC_LEN)); @@ -68,7 +63,7 @@ public class Rss20 extends Namespace { } catch (NumberFormatException e) { Log.d(TAG, "Length attribute could not be parsed."); } - FeedMedia media = new FeedMedia(state.getCurrentItem(), url, size, type); + FeedMedia media = new FeedMedia(state.getCurrentItem(), url, size, mimeType); state.getCurrentItem().setMedia(media); } } diff --git a/parser/feed/src/main/java/de/danoeh/antennapod/parser/feed/util/SyndTypeUtils.java b/parser/feed/src/main/java/de/danoeh/antennapod/parser/feed/util/SyndTypeUtils.java index 2e6cf864f..dfd7801a5 100644 --- a/parser/feed/src/main/java/de/danoeh/antennapod/parser/feed/util/SyndTypeUtils.java +++ b/parser/feed/src/main/java/de/danoeh/antennapod/parser/feed/util/SyndTypeUtils.java @@ -1,17 +1,32 @@ package de.danoeh.antennapod.parser.feed.util; import android.webkit.MimeTypeMap; +import androidx.annotation.Nullable; import org.apache.commons.io.FilenameUtils; /** * Utility class for handling MIME-Types of enclosures. * */ public class SyndTypeUtils { + public static final String OCTET_STREAM = "application/octet-stream"; + private SyndTypeUtils() { } - public static boolean enclosureTypeValid(String type) { + @Nullable + public static String getMimeType(@Nullable String type, @Nullable String filename) { + if (isMediaFile(type) && !OCTET_STREAM.equals(type)) { + return type; + } + String filenameType = SyndTypeUtils.getMimeTypeFromUrl(filename); + if (isMediaFile(filenameType)) { + return filenameType; + } + return type; + } + + public static boolean isMediaFile(String type) { if (type == null) { return false; } else { @@ -22,7 +37,7 @@ public class SyndTypeUtils { } } - public static boolean imageTypeValid(String type) { + public static boolean isImageFile(String type) { if (type == null) { return false; } else { @@ -34,7 +49,7 @@ public class SyndTypeUtils { * Should be used if mime-type of enclosure tag is not supported. This * method will return the mime-type of the file extension. */ - public static String getMimeTypeFromUrl(String url) { + private static String getMimeTypeFromUrl(String url) { if (url == null) { return null; } From d4d506345b222406a41ddd2a49af5c208f76e236 Mon Sep 17 00:00:00 2001 From: ByteHamster Date: Tue, 25 Jan 2022 22:08:05 +0100 Subject: [PATCH 2/2] Move mime type guessing out of MediaType enum --- .../core/feed/LocalFeedUpdater.java | 18 ++--------- .../antennapod/model/playback/MediaType.java | 26 --------------- .../parser/feed/namespace/Atom.java | 7 ++-- .../parser/feed/namespace/Media.java | 9 +++--- .../parser/feed/namespace/Rss20.java | 7 ++-- ...{SyndTypeUtils.java => MimeTypeUtils.java} | 32 ++++++++++++++++--- 6 files changed, 41 insertions(+), 58 deletions(-) rename parser/feed/src/main/java/de/danoeh/antennapod/parser/feed/util/{SyndTypeUtils.java => MimeTypeUtils.java} (58%) diff --git a/core/src/main/java/de/danoeh/antennapod/core/feed/LocalFeedUpdater.java b/core/src/main/java/de/danoeh/antennapod/core/feed/LocalFeedUpdater.java index 5d685c24f..7ddaa080a 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/feed/LocalFeedUpdater.java +++ b/core/src/main/java/de/danoeh/antennapod/core/feed/LocalFeedUpdater.java @@ -34,6 +34,7 @@ import de.danoeh.antennapod.model.feed.FeedItem; import de.danoeh.antennapod.model.feed.FeedMedia; import de.danoeh.antennapod.model.feed.FeedPreferences; import de.danoeh.antennapod.model.playback.MediaType; +import de.danoeh.antennapod.parser.feed.util.MimeTypeUtils; public class LocalFeedUpdater { @@ -74,21 +75,8 @@ public class LocalFeedUpdater { List mediaFiles = new ArrayList<>(); Set mediaFileNames = new HashSet<>(); for (DocumentFile file : documentFolder.listFiles()) { - String mime = file.getType(); - if (mime == null) { - continue; - } - - MediaType mediaType = MediaType.fromMimeType(mime); - if (mediaType == MediaType.UNKNOWN) { - String path = file.getUri().toString(); - int fileExtensionPosition = path.lastIndexOf('.'); - if (fileExtensionPosition >= 0) { - String extensionWithoutDot = path.substring(fileExtensionPosition + 1); - mediaType = MediaType.fromFileExtension(extensionWithoutDot); - } - } - + String mimeType = MimeTypeUtils.getMimeType(file.getType(), file.getUri().toString()); + MediaType mediaType = MediaType.fromMimeType(mimeType); if (mediaType == MediaType.AUDIO || mediaType == MediaType.VIDEO) { mediaFiles.add(file); mediaFileNames.add(file.getName()); diff --git a/model/src/main/java/de/danoeh/antennapod/model/playback/MediaType.java b/model/src/main/java/de/danoeh/antennapod/model/playback/MediaType.java index 6a7b36097..799977e9a 100644 --- a/model/src/main/java/de/danoeh/antennapod/model/playback/MediaType.java +++ b/model/src/main/java/de/danoeh/antennapod/model/playback/MediaType.java @@ -15,16 +15,6 @@ public enum MediaType { "application/x-flac" )); - // based on https://developer.android.com/guide/topics/media/media-formats - static final Set AUDIO_FILE_EXTENSIONS = new HashSet<>(Arrays.asList( - "3gp", "aac", "amr", "flac", "imy", "m4a", "mid", "mkv", "mp3", "mp4", "mxmf", "oga", - "ogg", "ogx", "opus", "ota", "rtttl", "rtx", "wav", "xmf" - )); - - static final Set VIDEO_FILE_EXTENSIONS = new HashSet<>(Arrays.asList( - "3gp", "mkv", "mp4", "ogg", "ogv", "ogx", "webm" - )); - public static MediaType fromMimeType(String mimeType) { if (TextUtils.isEmpty(mimeType)) { return MediaType.UNKNOWN; @@ -37,20 +27,4 @@ public enum MediaType { } return MediaType.UNKNOWN; } - - /** - * @param extensionWithoutDot the file extension (suffix) without the dot - * @return the {@link MediaType} that likely corresponds to the extension. However, since the - * extension is not always enough to determine whether a file is an audio or video (3gp - * can be both, for example), this may not be correct. As a result, where possible, - * {@link #fromMimeType(String) fromMimeType} should always be tried first. - */ - public static MediaType fromFileExtension(String extensionWithoutDot) { - if (AUDIO_FILE_EXTENSIONS.contains(extensionWithoutDot)) { - return MediaType.AUDIO; - } else if (VIDEO_FILE_EXTENSIONS.contains(extensionWithoutDot)) { - return MediaType.VIDEO; - } - return MediaType.UNKNOWN; - } } diff --git a/parser/feed/src/main/java/de/danoeh/antennapod/parser/feed/namespace/Atom.java b/parser/feed/src/main/java/de/danoeh/antennapod/parser/feed/namespace/Atom.java index 7e2f00607..a79556c2c 100644 --- a/parser/feed/src/main/java/de/danoeh/antennapod/parser/feed/namespace/Atom.java +++ b/parser/feed/src/main/java/de/danoeh/antennapod/parser/feed/namespace/Atom.java @@ -13,7 +13,7 @@ import org.xml.sax.Attributes; import de.danoeh.antennapod.model.feed.FeedItem; import de.danoeh.antennapod.model.feed.FeedMedia; import de.danoeh.antennapod.parser.feed.element.SyndElement; -import de.danoeh.antennapod.parser.feed.util.SyndTypeUtils; +import de.danoeh.antennapod.parser.feed.util.MimeTypeUtils; public class Atom extends Namespace { private static final String TAG = "NSAtom"; @@ -91,11 +91,10 @@ public class Atom extends Namespace { } catch (NumberFormatException e) { Log.d(TAG, "Length attribute could not be parsed."); } - String type = attributes.getValue(LINK_TYPE); - String mimeType = SyndTypeUtils.getMimeType(type, href); + String mimeType = MimeTypeUtils.getMimeType(attributes.getValue(LINK_TYPE), href); FeedItem currItem = state.getCurrentItem(); - if (SyndTypeUtils.isMediaFile(mimeType) && currItem != null && !currItem.hasMedia()) { + if (MimeTypeUtils.isMediaFile(mimeType) && currItem != null && !currItem.hasMedia()) { currItem.setMedia(new FeedMedia(currItem, href, size, mimeType)); } } else if (LINK_REL_PAYMENT.equals(rel)) { diff --git a/parser/feed/src/main/java/de/danoeh/antennapod/parser/feed/namespace/Media.java b/parser/feed/src/main/java/de/danoeh/antennapod/parser/feed/namespace/Media.java index 29d7d7999..85cafea84 100644 --- a/parser/feed/src/main/java/de/danoeh/antennapod/parser/feed/namespace/Media.java +++ b/parser/feed/src/main/java/de/danoeh/antennapod/parser/feed/namespace/Media.java @@ -11,7 +11,7 @@ import java.util.concurrent.TimeUnit; import de.danoeh.antennapod.model.feed.FeedMedia; import de.danoeh.antennapod.parser.feed.element.AtomText; -import de.danoeh.antennapod.parser.feed.util.SyndTypeUtils; +import de.danoeh.antennapod.parser.feed.util.MimeTypeUtils; /** Processes tags from the http://search.yahoo.com/mrss/ namespace. */ public class Media extends Namespace { @@ -43,13 +43,12 @@ public class Media extends Namespace { Attributes attributes) { if (CONTENT.equals(localName)) { String url = attributes.getValue(DOWNLOAD_URL); - String type = attributes.getValue(MIME_TYPE); String defaultStr = attributes.getValue(DEFAULT); String medium = attributes.getValue(MEDIUM); boolean validTypeMedia = false; boolean validTypeImage = false; boolean isDefault = "true".equals(defaultStr); - String mimeType = SyndTypeUtils.getMimeType(type, url); + String mimeType = MimeTypeUtils.getMimeType(attributes.getValue(MIME_TYPE), url); if (MEDIUM_AUDIO.equals(medium)) { validTypeMedia = true; @@ -63,9 +62,9 @@ public class Media extends Namespace { validTypeImage = true; mimeType = "image/*"; } else { - if (SyndTypeUtils.isMediaFile(mimeType)) { + if (MimeTypeUtils.isMediaFile(mimeType)) { validTypeMedia = true; - } else if (SyndTypeUtils.isImageFile(mimeType)) { + } else if (MimeTypeUtils.isImageFile(mimeType)) { validTypeImage = true; } } diff --git a/parser/feed/src/main/java/de/danoeh/antennapod/parser/feed/namespace/Rss20.java b/parser/feed/src/main/java/de/danoeh/antennapod/parser/feed/namespace/Rss20.java index ad5eb9de8..1c1ba9f53 100644 --- a/parser/feed/src/main/java/de/danoeh/antennapod/parser/feed/namespace/Rss20.java +++ b/parser/feed/src/main/java/de/danoeh/antennapod/parser/feed/namespace/Rss20.java @@ -12,7 +12,7 @@ import org.xml.sax.Attributes; import de.danoeh.antennapod.model.feed.FeedItem; import de.danoeh.antennapod.model.feed.FeedMedia; -import de.danoeh.antennapod.parser.feed.util.SyndTypeUtils; +import de.danoeh.antennapod.parser.feed.util.MimeTypeUtils; import java.util.Locale; @@ -46,13 +46,12 @@ public class Rss20 extends Namespace { state.getItems().add(state.getCurrentItem()); state.getCurrentItem().setFeed(state.getFeed()); } else if (ENCLOSURE.equals(localName) && ITEM.equals(state.getTagstack().peek().getName())) { - String type = attributes.getValue(ENC_TYPE); String url = attributes.getValue(ENC_URL); - String mimeType = SyndTypeUtils.getMimeType(type, url); + String mimeType = MimeTypeUtils.getMimeType(attributes.getValue(ENC_TYPE), url); boolean validUrl = !TextUtils.isEmpty(url); if (state.getCurrentItem() != null && state.getCurrentItem().getMedia() == null - && SyndTypeUtils.isMediaFile(mimeType) && validUrl) { + && MimeTypeUtils.isMediaFile(mimeType) && validUrl) { long size = 0; try { size = Long.parseLong(attributes.getValue(ENC_LEN)); diff --git a/parser/feed/src/main/java/de/danoeh/antennapod/parser/feed/util/SyndTypeUtils.java b/parser/feed/src/main/java/de/danoeh/antennapod/parser/feed/util/MimeTypeUtils.java similarity index 58% rename from parser/feed/src/main/java/de/danoeh/antennapod/parser/feed/util/SyndTypeUtils.java rename to parser/feed/src/main/java/de/danoeh/antennapod/parser/feed/util/MimeTypeUtils.java index dfd7801a5..be1048050 100644 --- a/parser/feed/src/main/java/de/danoeh/antennapod/parser/feed/util/SyndTypeUtils.java +++ b/parser/feed/src/main/java/de/danoeh/antennapod/parser/feed/util/MimeTypeUtils.java @@ -4,13 +4,27 @@ import android.webkit.MimeTypeMap; import androidx.annotation.Nullable; import org.apache.commons.io.FilenameUtils; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + /** * Utility class for handling MIME-Types of enclosures. * */ -public class SyndTypeUtils { +public class MimeTypeUtils { public static final String OCTET_STREAM = "application/octet-stream"; - private SyndTypeUtils() { + // based on https://developer.android.com/guide/topics/media/media-formats + static final Set AUDIO_FILE_EXTENSIONS = new HashSet<>(Arrays.asList( + "3gp", "aac", "amr", "flac", "imy", "m4a", "mid", "mkv", "mp3", "mp4", "mxmf", "oga", + "ogg", "ogx", "opus", "ota", "rtttl", "rtx", "wav", "xmf" + )); + + static final Set VIDEO_FILE_EXTENSIONS = new HashSet<>(Arrays.asList( + "3gp", "mkv", "mp4", "ogg", "ogv", "ogx", "webm" + )); + + private MimeTypeUtils() { } @@ -19,7 +33,7 @@ public class SyndTypeUtils { if (isMediaFile(type) && !OCTET_STREAM.equals(type)) { return type; } - String filenameType = SyndTypeUtils.getMimeTypeFromUrl(filename); + String filenameType = MimeTypeUtils.getMimeTypeFromUrl(filename); if (isMediaFile(filenameType)) { return filenameType; } @@ -54,6 +68,16 @@ public class SyndTypeUtils { return null; } String extension = FilenameUtils.getExtension(url); - return MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension); + String mapResult = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension); + if (mapResult != null) { + return mapResult; + } + + if (AUDIO_FILE_EXTENSIONS.contains(extension)) { + return "audio/*"; + } else if (VIDEO_FILE_EXTENSIONS.contains(extension)) { + return "video/*"; + } + return null; } }