From 341f1b37d6c02859e2b48437817dcf7f4efdd374 Mon Sep 17 00:00:00 2001 From: Docile-Alligator <25734209+Docile-Alligator@users.noreply.github.com> Date: Mon, 16 Sep 2024 20:53:58 -0400 Subject: Use VideoLinkFetcher to load the video link in posts. --- .../infinityforreddit/FetchVideoLinkListener.java | 16 ++ .../infinityforreddit/VideoLinkFetcher.java | 121 +++++++++ .../activities/FilteredPostsActivity.java | 1 + .../activities/ViewImageOrGifActivity.java | 8 - .../activities/ViewImgurMediaActivity.java | 2 +- .../activities/ViewVideoActivity.java | 278 +++++++++------------ .../adapters/HistoryPostRecyclerViewAdapter.java | 38 +-- .../adapters/PostDetailRecyclerViewAdapter.java | 13 +- .../adapters/PostRecyclerViewAdapter.java | 50 ++-- .../fragments/ViewImgurImageFragment.java | 6 - .../fragments/ViewImgurVideoFragment.java | 6 - .../ViewRedditGalleryImageOrGifFragment.java | 8 - .../fragments/ViewRedditGalleryVideoFragment.java | 8 - .../post/FetchStreamableVideo.java | 26 +- .../services/DownloadMediaService.java | 87 +++++++ .../thing/FetchRedgifsVideoLinks.java | 29 +-- 16 files changed, 421 insertions(+), 276 deletions(-) create mode 100644 app/src/main/java/ml/docilealligator/infinityforreddit/FetchVideoLinkListener.java create mode 100644 app/src/main/java/ml/docilealligator/infinityforreddit/VideoLinkFetcher.java diff --git a/app/src/main/java/ml/docilealligator/infinityforreddit/FetchVideoLinkListener.java b/app/src/main/java/ml/docilealligator/infinityforreddit/FetchVideoLinkListener.java new file mode 100644 index 00000000..79132a60 --- /dev/null +++ b/app/src/main/java/ml/docilealligator/infinityforreddit/FetchVideoLinkListener.java @@ -0,0 +1,16 @@ +package ml.docilealligator.infinityforreddit; + +import androidx.annotation.Nullable; + +import ml.docilealligator.infinityforreddit.post.Post; +import ml.docilealligator.infinityforreddit.thing.StreamableVideo; + +public interface FetchVideoLinkListener { + default void onFetchRedditVideoLinkSuccess(Post post, String fileName) {} + default void onFetchImgurVideoLinkSuccess(String videoUrl, String videoDownloadUrl, String fileName) {} + default void onFetchRedgifsVideoLinkSuccess(String webm, String mp4) {} + default void onFetchStreamableVideoLinkSuccess(StreamableVideo streamableVideo) {} + default void onChangeFileName(String fileName) {} + default void onFetchVideoFallbackDirectUrlSuccess(String videoFallbackDirectUrl) {} + default void failed(@Nullable Integer messageRes) {} +} diff --git a/app/src/main/java/ml/docilealligator/infinityforreddit/VideoLinkFetcher.java b/app/src/main/java/ml/docilealligator/infinityforreddit/VideoLinkFetcher.java new file mode 100644 index 00000000..19d7e04b --- /dev/null +++ b/app/src/main/java/ml/docilealligator/infinityforreddit/VideoLinkFetcher.java @@ -0,0 +1,121 @@ +package ml.docilealligator.infinityforreddit; + +import android.content.SharedPreferences; +import android.net.Uri; +import android.os.Handler; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.annotation.OptIn; +import androidx.media3.common.util.UnstableApi; + +import org.apache.commons.io.FilenameUtils; + +import java.util.List; +import java.util.concurrent.Executor; + +import javax.inject.Provider; + +import ml.docilealligator.infinityforreddit.account.Account; +import ml.docilealligator.infinityforreddit.activities.ViewVideoActivity; +import ml.docilealligator.infinityforreddit.apis.StreamableAPI; +import ml.docilealligator.infinityforreddit.apis.VReddIt; +import ml.docilealligator.infinityforreddit.post.FetchPost; +import ml.docilealligator.infinityforreddit.post.FetchStreamableVideo; +import ml.docilealligator.infinityforreddit.post.Post; +import ml.docilealligator.infinityforreddit.thing.FetchRedgifsVideoLinks; +import retrofit2.Call; +import retrofit2.Callback; +import retrofit2.Response; +import retrofit2.Retrofit; + +public class VideoLinkFetcher { + public static void fetchVideoLink(Executor executor, Handler handler, Retrofit retrofit, Retrofit vReddItRetrofit, + Retrofit redgifsRetrofit, Provider streamableApiProvider, + SharedPreferences currentAccountSharedPreferences, int videoType, + @Nullable String redgifsId, @Nullable String vRedditItUrl, + @Nullable String shortCode, + FetchVideoLinkListener fetchVideoLinkListener) { + switch (videoType) { + case ViewVideoActivity.VIDEO_TYPE_STREAMABLE: + FetchStreamableVideo.fetchStreamableVideo(executor, handler, streamableApiProvider, shortCode, fetchVideoLinkListener); + break; + case ViewVideoActivity.VIDEO_TYPE_REDGIFS: + FetchRedgifsVideoLinks.fetchRedgifsVideoLinks(executor, handler, redgifsRetrofit, + currentAccountSharedPreferences, redgifsId, fetchVideoLinkListener); + break; + case ViewVideoActivity.VIDEO_TYPE_V_REDD_IT: + loadVReddItVideo(executor, handler, retrofit, vReddItRetrofit, redgifsRetrofit, streamableApiProvider, + currentAccountSharedPreferences, vRedditItUrl, fetchVideoLinkListener); + break; + } + } + + public static void loadVReddItVideo(Executor executor, Handler handler, Retrofit retrofit, Retrofit mVReddItRetrofit, + Retrofit redgifsRetrofit, Provider streamableApiProvider, + SharedPreferences currentAccountSharedPreferences, + String vRedditItUrl, FetchVideoLinkListener fetchVideoLinkListener) { + mVReddItRetrofit.create(VReddIt.class).getRedirectUrl(vRedditItUrl).enqueue(new Callback<>() { + @Override + public void onResponse(@NonNull Call call, @NonNull Response response) { + if (response.isSuccessful()) { + Uri redirectUri = Uri.parse(response.raw().request().url().toString()); + String redirectPath = redirectUri.getPath(); + if (redirectPath != null && (redirectPath.matches("/r/\\w+/comments/\\w+/?\\w+/?") || redirectPath.matches("/user/\\w+/comments/\\w+/?\\w+/?"))) { + List segments = redirectUri.getPathSegments(); + int commentsIndex = segments.lastIndexOf("comments"); + String postId = segments.get(commentsIndex + 1); + FetchPost.fetchPost(executor, handler, retrofit, postId, null, Account.ANONYMOUS_ACCOUNT, + new FetchPost.FetchPostListener() { + @OptIn(markerClass = UnstableApi.class) + @Override + public void fetchPostSuccess(Post post) { + fetchVideoLinkListener.onFetchVideoFallbackDirectUrlSuccess(post.getVideoFallBackDirectUrl()); + if (post.isRedgifs()) { + String redgifsId = post.getRedgifsId(); + if (redgifsId != null && redgifsId.contains("-")) { + redgifsId = redgifsId.substring(0, redgifsId.indexOf('-')); + } + fetchVideoLinkListener.onChangeFileName("Redgifs-" + redgifsId + ".mp4"); + + FetchRedgifsVideoLinks.fetchRedgifsVideoLinks(executor, handler, redgifsRetrofit, + currentAccountSharedPreferences, redgifsId, fetchVideoLinkListener); + } else if (post.isStreamable()) { + String shortCode = post.getStreamableShortCode(); + fetchVideoLinkListener.onChangeFileName("Streamable-" + shortCode + ".mp4"); + + FetchStreamableVideo.fetchStreamableVideo(executor, handler, streamableApiProvider, shortCode, fetchVideoLinkListener); + } else if (post.isImgur()) { + String videoDownloadUrl = post.getVideoDownloadUrl(); + String videoFileName = "imgur-" + FilenameUtils.getName(videoDownloadUrl); + fetchVideoLinkListener.onFetchImgurVideoLinkSuccess(post.getVideoUrl(), post.getVideoDownloadUrl(), videoFileName); + } else { + if (post.getVideoUrl() != null) { + String videoFileName = post.getSubredditName() + "-" + post.getId() + ".mp4"; + fetchVideoLinkListener.onFetchRedditVideoLinkSuccess(post, videoFileName); + } else { + fetchVideoLinkListener.failed(R.string.error_fetching_v_redd_it_video_cannot_get_video_url); + } + } + } + + @Override + public void fetchPostFailed() { + fetchVideoLinkListener.failed(R.string.error_fetching_v_redd_it_video_cannot_get_post); + } + }); + } else { + fetchVideoLinkListener.failed(R.string.error_fetching_v_redd_it_video_cannot_get_post_id); + } + } else { + fetchVideoLinkListener.failed(R.string.error_fetching_v_redd_it_video_cannot_get_redirect_url); + } + } + + @Override + public void onFailure(@NonNull Call call, @NonNull Throwable t) { + fetchVideoLinkListener.failed(R.string.error_fetching_v_redd_it_video_cannot_get_redirect_url); + } + }); + } +} diff --git a/app/src/main/java/ml/docilealligator/infinityforreddit/activities/FilteredPostsActivity.java b/app/src/main/java/ml/docilealligator/infinityforreddit/activities/FilteredPostsActivity.java index 13b2c3aa..a350219b 100644 --- a/app/src/main/java/ml/docilealligator/infinityforreddit/activities/FilteredPostsActivity.java +++ b/app/src/main/java/ml/docilealligator/infinityforreddit/activities/FilteredPostsActivity.java @@ -159,6 +159,7 @@ public class FilteredPostsActivity extends BaseActivity implements SortTypeSelec postFilter.containGalleryType = false; break; case Post.LINK_TYPE: + case Post.NO_PREVIEW_LINK_TYPE: postFilter.containTextType = false; postFilter.containLinkType = true; postFilter.containImageType = false; diff --git a/app/src/main/java/ml/docilealligator/infinityforreddit/activities/ViewImageOrGifActivity.java b/app/src/main/java/ml/docilealligator/infinityforreddit/activities/ViewImageOrGifActivity.java index fe4778c2..f828d0dc 100644 --- a/app/src/main/java/ml/docilealligator/infinityforreddit/activities/ViewImageOrGifActivity.java +++ b/app/src/main/java/ml/docilealligator/infinityforreddit/activities/ViewImageOrGifActivity.java @@ -359,14 +359,6 @@ public class ViewImageOrGifActivity extends AppCompatActivity implements SetAsWa private void download() { isDownloading = false; - /*Intent intent = new Intent(this, DownloadMediaService.class); - intent.putExtra(DownloadMediaService.EXTRA_URL, mImageUrl); - intent.putExtra(DownloadMediaService.EXTRA_MEDIA_TYPE, isGif ? DownloadMediaService.EXTRA_MEDIA_TYPE_GIF : DownloadMediaService.EXTRA_MEDIA_TYPE_IMAGE); - intent.putExtra(DownloadMediaService.EXTRA_FILE_NAME, mImageFileName); - intent.putExtra(DownloadMediaService.EXTRA_SUBREDDIT_NAME, mSubredditName); - intent.putExtra(DownloadMediaService.EXTRA_IS_NSFW, isNsfw); - ContextCompat.startForegroundService(this, intent);*/ - PersistableBundle extras = new PersistableBundle(); extras.putString(DownloadMediaService.EXTRA_URL, mImageUrl); extras.putInt(DownloadMediaService.EXTRA_MEDIA_TYPE, isGif ? DownloadMediaService.EXTRA_MEDIA_TYPE_GIF : DownloadMediaService.EXTRA_MEDIA_TYPE_IMAGE); diff --git a/app/src/main/java/ml/docilealligator/infinityforreddit/activities/ViewImgurMediaActivity.java b/app/src/main/java/ml/docilealligator/infinityforreddit/activities/ViewImgurMediaActivity.java index 8cf05256..25c78a31 100644 --- a/app/src/main/java/ml/docilealligator/infinityforreddit/activities/ViewImgurMediaActivity.java +++ b/app/src/main/java/ml/docilealligator/infinityforreddit/activities/ViewImgurMediaActivity.java @@ -166,7 +166,7 @@ public class ViewImgurMediaActivity extends AppCompatActivity implements SetAsWa switch (getIntent().getIntExtra(EXTRA_IMGUR_TYPE, IMGUR_TYPE_IMAGE)) { case IMGUR_TYPE_GALLERY: imgurRetrofit.create(ImgurAPI.class).getGalleryImages(APIUtils.IMGUR_CLIENT_ID, imgurId) - .enqueue(new Callback() { + .enqueue(new Callback<>() { @Override public void onResponse(@NonNull Call call, @NonNull Response response) { if (response.isSuccessful()) { diff --git a/app/src/main/java/ml/docilealligator/infinityforreddit/activities/ViewVideoActivity.java b/app/src/main/java/ml/docilealligator/infinityforreddit/activities/ViewVideoActivity.java index 3486018c..e878196e 100644 --- a/app/src/main/java/ml/docilealligator/infinityforreddit/activities/ViewVideoActivity.java +++ b/app/src/main/java/ml/docilealligator/infinityforreddit/activities/ViewVideoActivity.java @@ -37,6 +37,7 @@ import android.widget.Toast; import androidx.activity.OnBackPressedCallback; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import androidx.annotation.OptIn; import androidx.appcompat.app.ActionBar; import androidx.appcompat.app.AlertDialog; @@ -81,7 +82,6 @@ import com.otaliastudios.zoom.ZoomSurfaceView; import org.apache.commons.io.FilenameUtils; -import java.util.List; import java.util.concurrent.Executor; import javax.inject.Inject; @@ -90,14 +90,11 @@ import javax.inject.Provider; import app.futured.hauler.DragDirection; import ml.docilealligator.infinityforreddit.CustomFontReceiver; -import ml.docilealligator.infinityforreddit.thing.FetchRedgifsVideoLinks; -import ml.docilealligator.infinityforreddit.post.FetchStreamableVideo; +import ml.docilealligator.infinityforreddit.FetchVideoLinkListener; import ml.docilealligator.infinityforreddit.Infinity; import ml.docilealligator.infinityforreddit.R; -import ml.docilealligator.infinityforreddit.thing.StreamableVideo; -import ml.docilealligator.infinityforreddit.account.Account; +import ml.docilealligator.infinityforreddit.VideoLinkFetcher; import ml.docilealligator.infinityforreddit.apis.StreamableAPI; -import ml.docilealligator.infinityforreddit.apis.VReddIt; import ml.docilealligator.infinityforreddit.bottomsheetfragments.PlaybackSpeedBottomSheetFragment; import ml.docilealligator.infinityforreddit.customtheme.CustomThemeWrapper; import ml.docilealligator.infinityforreddit.databinding.ActivityViewVideoBinding; @@ -108,16 +105,13 @@ import ml.docilealligator.infinityforreddit.font.FontFamily; import ml.docilealligator.infinityforreddit.font.FontStyle; import ml.docilealligator.infinityforreddit.font.TitleFontFamily; import ml.docilealligator.infinityforreddit.font.TitleFontStyle; -import ml.docilealligator.infinityforreddit.post.FetchPost; import ml.docilealligator.infinityforreddit.post.Post; import ml.docilealligator.infinityforreddit.services.DownloadMediaService; import ml.docilealligator.infinityforreddit.services.DownloadRedditVideoService; +import ml.docilealligator.infinityforreddit.thing.StreamableVideo; import ml.docilealligator.infinityforreddit.utils.APIUtils; import ml.docilealligator.infinityforreddit.utils.SharedPreferencesUtils; import ml.docilealligator.infinityforreddit.utils.Utils; -import retrofit2.Call; -import retrofit2.Callback; -import retrofit2.Response; import retrofit2.Retrofit; public class ViewVideoActivity extends AppCompatActivity implements CustomFontReceiver { @@ -135,11 +129,11 @@ public class ViewVideoActivity extends AppCompatActivity implements CustomFontRe public static final String EXTRA_ID = "EI"; public static final String EXTRA_POST = "EP"; public static final String EXTRA_PROGRESS_SECONDS = "EPS"; - public static final String EXTRA_VIDEO_TYPE = "EVT"; public static final String EXTRA_REDGIFS_ID = "EGI"; public static final String EXTRA_V_REDD_IT_URL = "EVRIU"; public static final String EXTRA_STREAMABLE_SHORT_CODE = "ESSC"; public static final String EXTRA_IS_NSFW = "EIN"; + public static final String EXTRA_VIDEO_TYPE = "EVT"; public static final int VIDEO_TYPE_IMGUR = 7; public static final int VIDEO_TYPE_STREAMABLE = 5; public static final int VIDEO_TYPE_V_REDD_IT = 4; @@ -601,6 +595,7 @@ public class ViewVideoActivity extends AppCompatActivity implements CustomFontRe // Produces DataSource instances through which media data is loaded. dataSourceFactory = new CacheDataSource.Factory().setCache(mSimpleCache) .setUpstreamDataSourceFactory(new DefaultHttpDataSource.Factory().setAllowCrossProtocolRedirects(true).setUserAgent(APIUtils.USER_AGENT)); + String redgifsId = null; if (videoType == VIDEO_TYPE_STREAMABLE) { if (savedInstanceState != null) { videoDownloadUrl = savedInstanceState.getString(VIDEO_DOWNLOAD_URL_STATE); @@ -610,15 +605,6 @@ public class ViewVideoActivity extends AppCompatActivity implements CustomFontRe String shortCode = intent.getStringExtra(EXTRA_STREAMABLE_SHORT_CODE); videoFileName = "Streamable-" + shortCode + ".mp4"; - if (mVideoUri == null) { - loadStreamableVideo(shortCode, savedInstanceState); - } else { - player.prepare(); - player.setMediaSource(new ProgressiveMediaSource.Factory(dataSourceFactory).createMediaSource(MediaItem.fromUri(mVideoUri))); - preparePlayer(savedInstanceState); - } - } else if (videoType == VIDEO_TYPE_V_REDD_IT) { - loadVReddItVideo(savedInstanceState); } else if (videoType == VIDEO_TYPE_REDGIFS) { if (savedInstanceState != null) { videoDownloadUrl = savedInstanceState.getString(VIDEO_DOWNLOAD_URL_STATE); @@ -626,19 +612,11 @@ public class ViewVideoActivity extends AppCompatActivity implements CustomFontRe videoDownloadUrl = intent.getStringExtra(EXTRA_VIDEO_DOWNLOAD_URL); } - String redgifsId = intent.getStringExtra(EXTRA_REDGIFS_ID); + redgifsId = intent.getStringExtra(EXTRA_REDGIFS_ID); if (redgifsId != null && redgifsId.contains("-")) { redgifsId = redgifsId.substring(0, redgifsId.indexOf('-')); } videoFileName = "Redgifs-" + redgifsId + ".mp4"; - - if (mVideoUri == null) { - loadRedgifsVideo(redgifsId, savedInstanceState); - } else { - player.prepare(); - player.setMediaSource(new ProgressiveMediaSource.Factory(dataSourceFactory).createMediaSource(MediaItem.fromUri(mVideoUri))); - preparePlayer(savedInstanceState); - } } else if (videoType == VIDEO_TYPE_DIRECT || videoType == VIDEO_TYPE_IMGUR) { videoDownloadUrl = mVideoUri.toString(); if (videoType == VIDEO_TYPE_DIRECT) { @@ -646,19 +624,117 @@ public class ViewVideoActivity extends AppCompatActivity implements CustomFontRe } else { videoFileName = "imgur-" + FilenameUtils.getName(videoDownloadUrl); } - // Prepare the player with the source. - player.prepare(); - player.setMediaSource(new ProgressiveMediaSource.Factory(dataSourceFactory).createMediaSource(MediaItem.fromUri(mVideoUri))); - preparePlayer(savedInstanceState); } else { videoDownloadUrl = intent.getStringExtra(EXTRA_VIDEO_DOWNLOAD_URL); subredditName = intent.getStringExtra(EXTRA_SUBREDDIT); id = intent.getStringExtra(EXTRA_ID); videoFileName = subredditName + "-" + id + ".mp4"; - // Prepare the player with the source. - player.prepare(); - player.setMediaSource(new HlsMediaSource.Factory(dataSourceFactory).createMediaSource(MediaItem.fromUri(mVideoUri))); - preparePlayer(savedInstanceState); + } + + if (mVideoUri == null) { + binding.getProgressBar().setVisibility(View.VISIBLE); + + VideoLinkFetcher.fetchVideoLink(mExecutor, new Handler(getMainLooper()), mRetrofit, mVReddItRetrofit, + mRedgifsRetrofit, mStreamableApiProvider, mCurrentAccountSharedPreferences, videoType, + redgifsId, getIntent().getStringExtra(EXTRA_V_REDD_IT_URL), + intent.getStringExtra(EXTRA_STREAMABLE_SHORT_CODE), + new FetchVideoLinkListener() { + @Override + public void onFetchRedditVideoLinkSuccess(Post post, String fileName) { + videoType = VIDEO_TYPE_NORMAL; + videoFileName = fileName; + + binding.getProgressBar().setVisibility(View.GONE); + mVideoUri = Uri.parse(post.getVideoUrl()); + subredditName = post.getSubredditName(); + id = post.getId(); + ViewVideoActivity.this.videoDownloadUrl = post.getVideoDownloadUrl(); + + videoFileName = subredditName + "-" + id + ".mp4"; + // Prepare the player with the source. + preparePlayer(savedInstanceState); + player.prepare(); + player.setMediaSource(new HlsMediaSource.Factory(dataSourceFactory).createMediaSource(MediaItem.fromUri(mVideoUri))); + } + + @Override + public void onFetchImgurVideoLinkSuccess(String videoUrl, String videoDownloadUrl, String fileName) { + videoType = VIDEO_TYPE_IMGUR; + videoFileName = fileName; + + binding.getProgressBar().setVisibility(View.GONE); + mVideoUri = Uri.parse(videoUrl); + ViewVideoActivity.this.videoDownloadUrl = videoDownloadUrl; + videoFileName = "imgur-" + FilenameUtils.getName(videoDownloadUrl); + // Prepare the player with the source. + player.prepare(); + player.setMediaSource(new ProgressiveMediaSource.Factory(dataSourceFactory).createMediaSource(MediaItem.fromUri(mVideoUri))); + preparePlayer(savedInstanceState); + } + + @Override + public void onFetchRedgifsVideoLinkSuccess(String webm, String mp4) { + videoType = VIDEO_TYPE_REDGIFS; + + binding.getProgressBar().setVisibility(View.GONE); + mVideoUri = Uri.parse(webm); + videoDownloadUrl = mp4; + preparePlayer(savedInstanceState); + player.prepare(); + player.setMediaSource(new ProgressiveMediaSource.Factory(dataSourceFactory).createMediaSource(MediaItem.fromUri(mVideoUri))); + } + + @Override + public void onFetchStreamableVideoLinkSuccess(StreamableVideo streamableVideo) { + videoType = VIDEO_TYPE_STREAMABLE; + + binding.getProgressBar().setVisibility(View.GONE); + if (streamableVideo.mp4 == null && streamableVideo.mp4Mobile == null) { + Toast.makeText(ViewVideoActivity.this, R.string.fetch_streamable_video_failed, Toast.LENGTH_SHORT).show(); + return; + } + binding.getTitleTextView().setText(streamableVideo.title); + videoDownloadUrl = streamableVideo.mp4 == null ? streamableVideo.mp4Mobile.url : streamableVideo.mp4.url; + mVideoUri = Uri.parse(videoDownloadUrl); + preparePlayer(savedInstanceState); + player.prepare(); + player.setMediaSource(new ProgressiveMediaSource.Factory(dataSourceFactory).createMediaSource(MediaItem.fromUri(mVideoUri))); + } + + @Override + public void onChangeFileName(String fileName) { + videoFileName = fileName; + } + + @Override + public void onFetchVideoFallbackDirectUrlSuccess(String videoFallbackDirectUrl) { + ViewVideoActivity.this.videoFallbackDirectUrl = videoFallbackDirectUrl; + } + + @Override + public void failed(@Nullable Integer messageRes) { + binding.getProgressBar().setVisibility(View.GONE); + if (videoType == VIDEO_TYPE_V_REDD_IT) { + if (messageRes != null) { + Toast.makeText(ViewVideoActivity.this, messageRes, Toast.LENGTH_LONG).show(); + } + } else { + loadFallbackVideo(savedInstanceState); + } + } + }); + } else { + if (videoType == VIDEO_TYPE_NORMAL) { + // Prepare the player with the source. + player.prepare(); + player.setMediaSource(new HlsMediaSource.Factory(dataSourceFactory).createMediaSource(MediaItem.fromUri(mVideoUri))); + preparePlayer(savedInstanceState); + } else { + // Prepare the player with the source. + player.prepare(); + player.setMediaSource(new ProgressiveMediaSource.Factory(dataSourceFactory).createMediaSource(MediaItem.fromUri(mVideoUri))); + preparePlayer(savedInstanceState); + } } getOnBackPressedDispatcher().addCallback(new OnBackPressedCallback(true) { @@ -733,134 +809,6 @@ public class ViewVideoActivity extends AppCompatActivity implements CustomFontRe return C.TRACK_TYPE_UNKNOWN; } - private void loadRedgifsVideo(String redgifsId, Bundle savedInstanceState) { - binding.getProgressBar().setVisibility(View.VISIBLE); - FetchRedgifsVideoLinks.fetchRedgifsVideoLinks(mExecutor, new Handler(), mRedgifsRetrofit, - mCurrentAccountSharedPreferences, redgifsId, new FetchRedgifsVideoLinks.FetchRedgifsVideoLinksListener() { - @OptIn(markerClass = UnstableApi.class) - @Override - public void success(String webm, String mp4) { - binding.getProgressBar().setVisibility(View.GONE); - mVideoUri = Uri.parse(webm); - videoDownloadUrl = mp4; - preparePlayer(savedInstanceState); - player.prepare(); - player.setMediaSource(new ProgressiveMediaSource.Factory(dataSourceFactory).createMediaSource(MediaItem.fromUri(mVideoUri))); - } - - @Override - public void failed(int errorCode) { - binding.getProgressBar().setVisibility(View.GONE); - loadFallbackVideo(savedInstanceState); - //Toast.makeText(ViewVideoActivity.this, R.string.fetch_redgifs_video_failed, Toast.LENGTH_SHORT).show(); - } - }); - } - - private void loadVReddItVideo(Bundle savedInstanceState) { - binding.getProgressBar().setVisibility(View.VISIBLE); - mVReddItRetrofit.create(VReddIt.class).getRedirectUrl(getIntent().getStringExtra(EXTRA_V_REDD_IT_URL)).enqueue(new Callback<>() { - @Override - public void onResponse(@NonNull Call call, @NonNull Response response) { - Uri redirectUri = Uri.parse(response.raw().request().url().toString()); - String redirectPath = redirectUri.getPath(); - if (redirectPath != null && (redirectPath.matches("/r/\\w+/comments/\\w+/?\\w+/?") || redirectPath.matches("/user/\\w+/comments/\\w+/?\\w+/?"))) { - List segments = redirectUri.getPathSegments(); - int commentsIndex = segments.lastIndexOf("comments"); - String postId = segments.get(commentsIndex + 1); - FetchPost.fetchPost(mExecutor, new Handler(), mRetrofit, postId, null, Account.ANONYMOUS_ACCOUNT, - new FetchPost.FetchPostListener() { - @OptIn(markerClass = UnstableApi.class) - @Override - public void fetchPostSuccess(Post post) { - videoFallbackDirectUrl = post.getVideoFallBackDirectUrl(); - if (post.isRedgifs()) { - videoType = VIDEO_TYPE_REDGIFS; - String redgifsId = post.getRedgifsId(); - if (redgifsId != null && redgifsId.contains("-")) { - redgifsId = redgifsId.substring(0, redgifsId.indexOf('-')); - } - videoFileName = "Redgifs-" + redgifsId + ".mp4"; - loadRedgifsVideo(redgifsId, savedInstanceState); - } else if (post.isStreamable()) { - videoType = VIDEO_TYPE_STREAMABLE; - String shortCode = post.getStreamableShortCode(); - videoFileName = "Streamable-" + shortCode + ".mp4"; - loadStreamableVideo(shortCode, savedInstanceState); - } else if (post.isImgur()) { - mVideoUri = Uri.parse(post.getVideoUrl()); - videoDownloadUrl = post.getVideoDownloadUrl(); - videoType = VIDEO_TYPE_IMGUR; - videoFileName = "imgur-" + FilenameUtils.getName(videoDownloadUrl); - // Prepare the player with the source. - player.prepare(); - player.setMediaSource(new ProgressiveMediaSource.Factory(dataSourceFactory).createMediaSource(MediaItem.fromUri(mVideoUri))); - preparePlayer(savedInstanceState); - } else { - binding.getProgressBar().setVisibility(View.GONE); - if (post.getVideoUrl() != null) { - mVideoUri = Uri.parse(post.getVideoUrl()); - subredditName = post.getSubredditName(); - id = post.getId(); - videoDownloadUrl = post.getVideoDownloadUrl(); - - videoFileName = subredditName + "-" + id + ".mp4"; - // Prepare the player with the source. - preparePlayer(savedInstanceState); - player.prepare(); - player.setMediaSource(new HlsMediaSource.Factory(dataSourceFactory).createMediaSource(MediaItem.fromUri(mVideoUri))); - } else { - Toast.makeText(ViewVideoActivity.this, R.string.error_fetching_v_redd_it_video_cannot_get_video_url, Toast.LENGTH_LONG).show(); - } - } - } - - @Override - public void fetchPostFailed() { - Toast.makeText(ViewVideoActivity.this, R.string.error_fetching_v_redd_it_video_cannot_get_post, Toast.LENGTH_LONG).show(); - } - }); - } else { - Toast.makeText(ViewVideoActivity.this, R.string.error_fetching_v_redd_it_video_cannot_get_post_id, Toast.LENGTH_LONG).show(); - } - } - - @Override - public void onFailure(@NonNull Call call, @NonNull Throwable t) { - Toast.makeText(ViewVideoActivity.this, R.string.error_fetching_v_redd_it_video_cannot_get_redirect_url, Toast.LENGTH_LONG).show(); - } - }); - } - - private void loadStreamableVideo(String shortCode, Bundle savedInstanceState) { - binding.getProgressBar().setVisibility(View.VISIBLE); - FetchStreamableVideo.fetchStreamableVideo(mExecutor, new Handler(), mStreamableApiProvider, shortCode, - new FetchStreamableVideo.FetchStreamableVideoListener() { - @OptIn(markerClass = UnstableApi.class) - @Override - public void success(StreamableVideo streamableVideo) { - if (streamableVideo.mp4 == null && streamableVideo.mp4Mobile == null) { - Toast.makeText(ViewVideoActivity.this, R.string.fetch_streamable_video_failed, Toast.LENGTH_SHORT).show(); - return; - } - binding.getTitleTextView().setText(streamableVideo.title); - binding.getProgressBar().setVisibility(View.GONE); - videoDownloadUrl = streamableVideo.mp4 == null ? streamableVideo.mp4Mobile.url : streamableVideo.mp4.url; - mVideoUri = Uri.parse(videoDownloadUrl); - preparePlayer(savedInstanceState); - player.prepare(); - player.setMediaSource(new ProgressiveMediaSource.Factory(dataSourceFactory).createMediaSource(MediaItem.fromUri(mVideoUri))); - } - - @Override - public void failed() { - binding.getProgressBar().setVisibility(View.GONE); - loadFallbackVideo(savedInstanceState); - //Toast.makeText(ViewVideoActivity.this, R.string.fetch_streamable_video_failed, Toast.LENGTH_SHORT).show(); - } - }); - } - @OptIn(markerClass = UnstableApi.class) private void loadFallbackVideo(Bundle savedInstanceState) { if (videoFallbackDirectUrl != null) { diff --git a/app/src/main/java/ml/docilealligator/infinityforreddit/adapters/HistoryPostRecyclerViewAdapter.java b/app/src/main/java/ml/docilealligator/infinityforreddit/adapters/HistoryPostRecyclerViewAdapter.java index 2308cf3d..a7e01054 100644 --- a/app/src/main/java/ml/docilealligator/infinityforreddit/adapters/HistoryPostRecyclerViewAdapter.java +++ b/app/src/main/java/ml/docilealligator/infinityforreddit/adapters/HistoryPostRecyclerViewAdapter.java @@ -68,6 +68,7 @@ import javax.inject.Provider; import jp.wasabeef.glide.transformations.BlurTransformation; import jp.wasabeef.glide.transformations.RoundedCornersTransformation; +import ml.docilealligator.infinityforreddit.FetchVideoLinkListener; import ml.docilealligator.infinityforreddit.thing.FetchRedgifsVideoLinks; import ml.docilealligator.infinityforreddit.post.FetchStreamableVideo; import ml.docilealligator.infinityforreddit.R; @@ -785,9 +786,9 @@ public class HistoryPostRecyclerViewAdapter extends PagingDataAdapter streamableApiProvider, - String videoUrl, FetchStreamableVideoListener fetchStreamableVideoListener) { + String videoUrl, FetchVideoLinkListener fetchVideoLinkListener) { executor.execute(() -> { try { Response response = streamableApiProvider.get().getStreamableData(videoUrl).execute(); @@ -39,23 +35,23 @@ public class FetchStreamableVideo { mp4MobileTemp = parseMedia(filesObject.getJSONObject(JSONUtils.MP4_MOBILE_KEY)); } if (mp4 == null && mp4MobileTemp == null) { - handler.post(fetchStreamableVideoListener::failed); + handler.post(() -> fetchVideoLinkListener.failed(null)); return; } StreamableVideo.Media mp4Mobile = mp4MobileTemp; - handler.post(() -> fetchStreamableVideoListener.success(new StreamableVideo(title, mp4, mp4Mobile))); + handler.post(() -> fetchVideoLinkListener.onFetchStreamableVideoLinkSuccess(new StreamableVideo(title, mp4, mp4Mobile))); } else { - handler.post(fetchStreamableVideoListener::failed); + handler.post(() -> fetchVideoLinkListener.failed(null)); } } catch (IOException | JSONException e) { e.printStackTrace(); - handler.post(fetchStreamableVideoListener::failed); + handler.post(() -> fetchVideoLinkListener.failed(null)); } }); } public static void fetchStreamableVideoInRecyclerViewAdapter(Executor executor, Handler handler, Call streamableCall, - FetchStreamableVideoListener fetchStreamableVideoListener) { + FetchVideoLinkListener fetchVideoLinkListener) { executor.execute(() -> { try { Response response = streamableCall.execute(); @@ -69,17 +65,17 @@ public class FetchStreamableVideo { mp4MobileTemp = parseMedia(filesObject.getJSONObject(JSONUtils.MP4_MOBILE_KEY)); } if (mp4 == null && mp4MobileTemp == null) { - handler.post(fetchStreamableVideoListener::failed); + handler.post(() -> fetchVideoLinkListener.failed(null)); return; } StreamableVideo.Media mp4Mobile = mp4MobileTemp; - handler.post(() -> fetchStreamableVideoListener.success(new StreamableVideo(title, mp4, mp4Mobile))); + handler.post(() -> fetchVideoLinkListener.onFetchStreamableVideoLinkSuccess(new StreamableVideo(title, mp4, mp4Mobile))); } else { - handler.post(fetchStreamableVideoListener::failed); + handler.post(() -> fetchVideoLinkListener.failed(null)); } } catch (IOException | JSONException e) { e.printStackTrace(); - handler.post(fetchStreamableVideoListener::failed); + handler.post(() -> fetchVideoLinkListener.failed(null)); } }); } diff --git a/app/src/main/java/ml/docilealligator/infinityforreddit/services/DownloadMediaService.java b/app/src/main/java/ml/docilealligator/infinityforreddit/services/DownloadMediaService.java index fc781bc7..331026c3 100644 --- a/app/src/main/java/ml/docilealligator/infinityforreddit/services/DownloadMediaService.java +++ b/app/src/main/java/ml/docilealligator/infinityforreddit/services/DownloadMediaService.java @@ -31,6 +31,7 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.util.List; import java.util.Random; import java.util.concurrent.Executor; @@ -43,6 +44,8 @@ import ml.docilealligator.infinityforreddit.R; import ml.docilealligator.infinityforreddit.apis.DownloadFile; import ml.docilealligator.infinityforreddit.broadcastreceivers.DownloadedMediaDeleteActionBroadcastReceiver; import ml.docilealligator.infinityforreddit.customtheme.CustomThemeWrapper; +import ml.docilealligator.infinityforreddit.post.ImgurMedia; +import ml.docilealligator.infinityforreddit.post.Post; import ml.docilealligator.infinityforreddit.utils.APIUtils; import ml.docilealligator.infinityforreddit.utils.NotificationUtils; import ml.docilealligator.infinityforreddit.utils.SharedPreferencesUtils; @@ -83,6 +86,90 @@ public class DownloadMediaService extends JobService { public DownloadMediaService() { } + public static JobInfo constructJobInfo(Context context, long contentEstimatedBytes, Post post, int galleryIndex) { + PersistableBundle extras = new PersistableBundle(); + if (post.getPostType() == Post.IMAGE_TYPE) { + extras.putString(DownloadMediaService.EXTRA_URL, post.getUrl()); + extras.putInt(DownloadMediaService.EXTRA_MEDIA_TYPE, DownloadMediaService.EXTRA_MEDIA_TYPE_IMAGE); + extras.putString(DownloadMediaService.EXTRA_FILE_NAME, post.getSubredditName() + + "-" + post.getId() + ".jpg"); + extras.putString(DownloadMediaService.EXTRA_SUBREDDIT_NAME, post.getSubredditName()); + extras.putInt(DownloadMediaService.EXTRA_IS_NSFW, post.isNSFW() ? 1 : 0); + } else if (post.getPostType() == Post.GIF_TYPE) { + extras.putString(DownloadMediaService.EXTRA_URL, post.getVideoUrl()); + extras.putInt(DownloadMediaService.EXTRA_MEDIA_TYPE, DownloadMediaService.EXTRA_MEDIA_TYPE_GIF); + extras.putString(DownloadMediaService.EXTRA_FILE_NAME, post.getSubredditName() + "-" + post.getId() + ".gif"); + extras.putString(DownloadMediaService.EXTRA_SUBREDDIT_NAME, post.getSubredditName()); + extras.putInt(DownloadMediaService.EXTRA_IS_NSFW, post.isNSFW() ? 1 : 0); + } else if (post.getPostType() == Post.VIDEO_TYPE) { + if (post.isImgur() || post.isRedgifs() || post.isStreamable()) { + //TODO fetch download links first + /*extras.putString(DownloadMediaService.EXTRA_URL, post.getVideoDownloadUrl()); + extras.putInt(DownloadMediaService.EXTRA_MEDIA_TYPE, DownloadMediaService.EXTRA_MEDIA_TYPE_VIDEO); + extras.putString(DownloadMediaService.EXTRA_FILE_NAME, videoFileName); + + extras.putString(DownloadMediaService.EXTRA_SUBREDDIT_NAME, post.getSubredditName()); + extras.putInt(DownloadMediaService.EXTRA_IS_NSFW, post.isNSFW() ? 1 : 0);*/ + } + } else if (post.getPostType() == Post.GALLERY_TYPE) { + Post.Gallery media = post.getGallery().get(galleryIndex); + if (media.mediaType == Post.Gallery.TYPE_VIDEO) { + extras.putString(DownloadMediaService.EXTRA_URL, media.url); + extras.putInt(DownloadMediaService.EXTRA_MEDIA_TYPE, DownloadMediaService.EXTRA_MEDIA_TYPE_VIDEO); + extras.putString(DownloadMediaService.EXTRA_FILE_NAME, media.fileName); + extras.putString(DownloadMediaService.EXTRA_SUBREDDIT_NAME, post.getSubredditName()); + extras.putInt(DownloadMediaService.EXTRA_IS_NSFW, post.isNSFW() ? 1 : 0); + } else { + extras.putString(DownloadMediaService.EXTRA_URL, media.hasFallback() ? media.fallbackUrl : media.url); // Retrieve original instead of the one additionally compressed by reddit + extras.putInt(DownloadMediaService.EXTRA_MEDIA_TYPE, media.mediaType == Post.Gallery.TYPE_GIF ? DownloadMediaService.EXTRA_MEDIA_TYPE_GIF: DownloadMediaService.EXTRA_MEDIA_TYPE_IMAGE); + extras.putString(DownloadMediaService.EXTRA_FILE_NAME, media.fileName); + extras.putString(DownloadMediaService.EXTRA_SUBREDDIT_NAME, post.getSubredditName()); + extras.putInt(DownloadMediaService.EXTRA_IS_NSFW, post.isNSFW() ? 1 : 0); + } + } + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { + return new JobInfo.Builder(JOB_ID++, new ComponentName(context, DownloadMediaService.class)) + .setUserInitiated(true) + .setRequiredNetwork(new NetworkRequest.Builder().clearCapabilities().build()) + .setEstimatedNetworkBytes(0, contentEstimatedBytes + 500) + .setExtras(extras) + .build(); + } else { + return new JobInfo.Builder(JOB_ID++, new ComponentName(context, DownloadMediaService.class)) + .setOverrideDeadline(0) + .setExtras(extras) + .build(); + } + } + + public static JobInfo constructJobInfo(Context context, long contentEstimatedBytes, ImgurMedia imgurMedia) { + PersistableBundle extras = new PersistableBundle(); + if (imgurMedia.getType() == ImgurMedia.TYPE_VIDEO) { + extras.putString(DownloadMediaService.EXTRA_URL, imgurMedia.getLink()); + extras.putInt(DownloadMediaService.EXTRA_MEDIA_TYPE, DownloadMediaService.EXTRA_MEDIA_TYPE_VIDEO); + extras.putString(DownloadMediaService.EXTRA_FILE_NAME, imgurMedia.getFileName()); + } else { + extras.putString(DownloadMediaService.EXTRA_URL, imgurMedia.getLink()); + extras.putInt(DownloadMediaService.EXTRA_MEDIA_TYPE, DownloadMediaService.EXTRA_MEDIA_TYPE_IMAGE); + extras.putString(DownloadMediaService.EXTRA_FILE_NAME, imgurMedia.getFileName()); + } + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { + return new JobInfo.Builder(JOB_ID++, new ComponentName(context, DownloadMediaService.class)) + .setUserInitiated(true) + .setRequiredNetwork(new NetworkRequest.Builder().clearCapabilities().build()) + .setEstimatedNetworkBytes(0, contentEstimatedBytes + 500) + .setExtras(extras) + .build(); + } else { + return new JobInfo.Builder(JOB_ID++, new ComponentName(context, DownloadMediaService.class)) + .setOverrideDeadline(0) + .setExtras(extras) + .build(); + } + } + public static JobInfo constructJobInfo(Context context, long contentEstimatedBytes, PersistableBundle extras) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { return new JobInfo.Builder(JOB_ID++, new ComponentName(context, DownloadMediaService.class)) diff --git a/app/src/main/java/ml/docilealligator/infinityforreddit/thing/FetchRedgifsVideoLinks.java b/app/src/main/java/ml/docilealligator/infinityforreddit/thing/FetchRedgifsVideoLinks.java index 60fd028b..ee2fc7ad 100644 --- a/app/src/main/java/ml/docilealligator/infinityforreddit/thing/FetchRedgifsVideoLinks.java +++ b/app/src/main/java/ml/docilealligator/infinityforreddit/thing/FetchRedgifsVideoLinks.java @@ -9,6 +9,7 @@ import org.json.JSONObject; import java.io.IOException; import java.util.concurrent.Executor; +import ml.docilealligator.infinityforreddit.FetchVideoLinkListener; import ml.docilealligator.infinityforreddit.apis.RedgifsAPI; import ml.docilealligator.infinityforreddit.utils.APIUtils; import ml.docilealligator.infinityforreddit.utils.JSONUtils; @@ -18,59 +19,53 @@ import retrofit2.Response; import retrofit2.Retrofit; public class FetchRedgifsVideoLinks { - - public interface FetchRedgifsVideoLinksListener { - void success(String webm, String mp4); - void failed(int errorCode); - } - public static void fetchRedgifsVideoLinks(Executor executor, Handler handler, Retrofit redgifsRetrofit, SharedPreferences currentAccountSharedPreferences, String redgifsId, - FetchRedgifsVideoLinksListener fetchRedgifsVideoLinksListener) { + FetchVideoLinkListener fetchVideoLinkListener) { executor.execute(() -> { try { Response response = redgifsRetrofit.create(RedgifsAPI.class).getRedgifsData(APIUtils.getRedgifsOAuthHeader(currentAccountSharedPreferences.getString(SharedPreferencesUtils.REDGIFS_ACCESS_TOKEN, "")), redgifsId, APIUtils.USER_AGENT).execute(); if (response.isSuccessful()) { - parseRedgifsVideoLinks(handler, response.body(), fetchRedgifsVideoLinksListener); + parseRedgifsVideoLinks(handler, response.body(), fetchVideoLinkListener); } else { - handler.post(() -> fetchRedgifsVideoLinksListener.failed(response.code())); + handler.post(() -> fetchVideoLinkListener.failed(null)); } } catch (IOException e) { e.printStackTrace(); - handler.post(() -> fetchRedgifsVideoLinksListener.failed(-1)); + handler.post(() -> fetchVideoLinkListener.failed(null)); } }); } public static void fetchRedgifsVideoLinksInRecyclerViewAdapter(Executor executor, Handler handler, Call redgifsCall, - FetchRedgifsVideoLinksListener fetchRedgifsVideoLinksListener) { + FetchVideoLinkListener fetchVideoLinkListener) { executor.execute(() -> { try { Response response = redgifsCall.execute(); if (response.isSuccessful()) { - parseRedgifsVideoLinks(handler, response.body(), fetchRedgifsVideoLinksListener); + parseRedgifsVideoLinks(handler, response.body(), fetchVideoLinkListener); } else { - handler.post(() -> fetchRedgifsVideoLinksListener.failed(response.code())); + handler.post(() -> fetchVideoLinkListener.failed(null)); } } catch (IOException e) { e.printStackTrace(); - handler.post(() -> fetchRedgifsVideoLinksListener.failed(-1)); + handler.post(() -> fetchVideoLinkListener.failed(null)); } }); } private static void parseRedgifsVideoLinks(Handler handler, String response, - FetchRedgifsVideoLinksListener fetchRedgifsVideoLinksListener) { + FetchVideoLinkListener fetchVideoLinkListener) { try { String mp4 = new JSONObject(response).getJSONObject(JSONUtils.GIF_KEY).getJSONObject(JSONUtils.URLS_KEY) .getString(JSONUtils.HD_KEY); - handler.post(() -> fetchRedgifsVideoLinksListener.success(mp4, mp4)); + handler.post(() -> fetchVideoLinkListener.onFetchRedgifsVideoLinkSuccess(mp4, mp4)); } catch (JSONException e) { e.printStackTrace(); - handler.post(() -> fetchRedgifsVideoLinksListener.failed(-1)); + handler.post(() -> fetchVideoLinkListener.failed(null)); } } } -- cgit v1.2.3