diff options
author | Alex Ning <chineseperson5@gmail.com> | 2021-12-09 13:29:35 +0000 |
---|---|---|
committer | Alex Ning <chineseperson5@gmail.com> | 2021-12-09 13:29:35 +0000 |
commit | 1abc703d7edb51f990984ad6d897cd0e3cac8e3e (patch) | |
tree | 2fe480822f64944657c738602f2ed1d01cb23f78 /app/src/main/java/ml/docilealligator | |
parent | 35cbd2431899221d35fc449f34c4e7e5a78e4213 (diff) | |
download | infinity-for-reddit-1abc703d7edb51f990984ad6d897cd0e3cac8e3e.tar infinity-for-reddit-1abc703d7edb51f990984ad6d897cd0e3cac8e3e.tar.gz infinity-for-reddit-1abc703d7edb51f990984ad6d897cd0e3cac8e3e.tar.bz2 infinity-for-reddit-1abc703d7edb51f990984ad6d897cd0e3cac8e3e.tar.lz infinity-for-reddit-1abc703d7edb51f990984ad6d897cd0e3cac8e3e.tar.xz infinity-for-reddit-1abc703d7edb51f990984ad6d897cd0e3cac8e3e.tar.zst infinity-for-reddit-1abc703d7edb51f990984ad6d897cd0e3cac8e3e.zip |
Support Streamable.
Diffstat (limited to 'app/src/main/java/ml/docilealligator')
8 files changed, 196 insertions, 19 deletions
diff --git a/app/src/main/java/ml/docilealligator/infinityforreddit/AppModule.java b/app/src/main/java/ml/docilealligator/infinityforreddit/AppModule.java index 2b8351bb..f9cde414 100644 --- a/app/src/main/java/ml/docilealligator/infinityforreddit/AppModule.java +++ b/app/src/main/java/ml/docilealligator/infinityforreddit/AppModule.java @@ -168,6 +168,16 @@ class AppModule { } @Provides + @Named("streamable") + @Singleton + Retrofit provideStreamableRetrofit(@Named("default") OkHttpClient okHttpClient) { + return new Retrofit.Builder() + .baseUrl(APIUtils.STREAMABLE_API_BASE_URI) + .addConverterFactory(ScalarsConverterFactory.create()) + .build(); + } + + @Provides @Named("default") @Singleton OkHttpClient provideOkHttpClient(@Named("no_oauth") Retrofit retrofit, RedditDataRoomDatabase accountRoomDatabase, diff --git a/app/src/main/java/ml/docilealligator/infinityforreddit/FetchStreamableVideo.java b/app/src/main/java/ml/docilealligator/infinityforreddit/FetchStreamableVideo.java new file mode 100644 index 00000000..ac7c2116 --- /dev/null +++ b/app/src/main/java/ml/docilealligator/infinityforreddit/FetchStreamableVideo.java @@ -0,0 +1,57 @@ +package ml.docilealligator.infinityforreddit; + +import android.os.Handler; + +import androidx.annotation.Nullable; + +import org.json.JSONException; +import org.json.JSONObject; + +import java.io.IOException; +import java.util.concurrent.Executor; + +import ml.docilealligator.infinityforreddit.apis.StreamableAPI; +import ml.docilealligator.infinityforreddit.utils.JSONUtils; +import retrofit2.Response; +import retrofit2.Retrofit; + +public class FetchStreamableVideo { + public interface FetchStreamableVideoListener { + void success(StreamableVideo streamableVideo); + void failed(); + } + + public static void fetchStreamableVideo(Executor executor, Handler handler, Retrofit streamableRetrofit, + String videoUrl, FetchStreamableVideoListener fetchStreamableVideoListener) { + executor.execute(() -> { + try { + Response<String> response = streamableRetrofit.create(StreamableAPI.class).getStreamableData(videoUrl).execute(); + if (response.isSuccessful()) { + JSONObject jsonObject = new JSONObject(response.body()); + String title = jsonObject.getString(JSONUtils.TITLE_KEY); + JSONObject filesObject = jsonObject.getJSONObject(JSONUtils.FILES_KEY); + StreamableVideo.Media mp4 = parseMedia(filesObject.getJSONObject(JSONUtils.MP4_KEY)); + StreamableVideo.Media mp4Mobile = parseMedia(filesObject.getJSONObject(JSONUtils.MP4_MOBILE_KEY)); + handler.post(() -> fetchStreamableVideoListener.success(new StreamableVideo(title, mp4, mp4Mobile))); + } else { + handler.post(fetchStreamableVideoListener::failed); + } + } catch (IOException | JSONException e) { + e.printStackTrace(); + } + }); + } + + @Nullable + private static StreamableVideo.Media parseMedia(JSONObject jsonObject) { + try { + return new StreamableVideo.Media( + jsonObject.getString(JSONUtils.URL_KEY), + jsonObject.getInt(JSONUtils.WIDTH_KEY), + jsonObject.getInt(JSONUtils.HEIGHT_KEY)); + } catch (JSONException e) { + e.printStackTrace(); + return null; + } + } +} diff --git a/app/src/main/java/ml/docilealligator/infinityforreddit/StreamableVideo.java b/app/src/main/java/ml/docilealligator/infinityforreddit/StreamableVideo.java new file mode 100644 index 00000000..909e890c --- /dev/null +++ b/app/src/main/java/ml/docilealligator/infinityforreddit/StreamableVideo.java @@ -0,0 +1,29 @@ +package ml.docilealligator.infinityforreddit; + +import androidx.annotation.Nullable; + +public class StreamableVideo { + public String title; + @Nullable + public Media mp4; + @Nullable + public Media mp4Mobile; + + public StreamableVideo(String title, @Nullable Media mp4, @Nullable Media mp4Mobile) { + this.title = title; + this.mp4 = mp4; + this.mp4Mobile = mp4Mobile; + } + + public static class Media { + public String url; + public int width; + public int height; + + public Media(String url, int width, int height) { + this.url = url; + this.width = width; + this.height = height; + } + } +} diff --git a/app/src/main/java/ml/docilealligator/infinityforreddit/activities/LinkResolverActivity.java b/app/src/main/java/ml/docilealligator/infinityforreddit/activities/LinkResolverActivity.java index b16348c8..fe7c89c3 100644 --- a/app/src/main/java/ml/docilealligator/infinityforreddit/activities/LinkResolverActivity.java +++ b/app/src/main/java/ml/docilealligator/infinityforreddit/activities/LinkResolverActivity.java @@ -51,6 +51,7 @@ public class LinkResolverActivity extends AppCompatActivity { private static final String RPAN_BROADCAST_PATTERN = "/rpan/r/[\\w-]+/\\w+/?\\w+/?"; private static final String WIKI_PATTERN = "/[rR]/[\\w-]+/(wiki|w)?(?:/\\w+)+"; private static final String GOOGLE_AMP_PATTERN = "/amp/s/amp.reddit.com/.*"; + private static final String STREAMABLE_PATTERN = "/\\w+/?"; @Inject @Named("default") @@ -270,12 +271,22 @@ public class LinkResolverActivity extends AppCompatActivity { deepLinkError(uri); } } else if (authority.contains("google.com")) { - if ( path.matches(GOOGLE_AMP_PATTERN) ) { - String url = path.substring(11, path.length()); // skipping past amp straight to reddit + if (path.matches(GOOGLE_AMP_PATTERN)) { + String url = path.substring(11); handleUri(Uri.parse("https://" + url)); } else { deepLinkError(uri); } + } else if (authority.equals("streamable.com")) { + if (path.matches(STREAMABLE_PATTERN)) { + String shortCode = segments.get(0); + Intent intent = new Intent(this, ViewVideoActivity.class); + intent.putExtra(ViewVideoActivity.EXTRA_VIDEO_TYPE, ViewVideoActivity.VIDEO_TYPE_STREAMABLE); + intent.putExtra(ViewVideoActivity.EXTRA_STREAMABLE_SHORT_CODE, shortCode); + startActivity(intent); + } else { + deepLinkError(uri); + } } else { deepLinkError(uri); } 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 8fd09be5..93c9649e 100644 --- a/app/src/main/java/ml/docilealligator/infinityforreddit/activities/ViewVideoActivity.java +++ b/app/src/main/java/ml/docilealligator/infinityforreddit/activities/ViewVideoActivity.java @@ -82,8 +82,10 @@ import app.futured.hauler.LockableNestedScrollView; import butterknife.BindView; import butterknife.ButterKnife; import ml.docilealligator.infinityforreddit.FetchGfycatOrRedgifsVideoLinks; +import ml.docilealligator.infinityforreddit.FetchStreamableVideo; import ml.docilealligator.infinityforreddit.Infinity; import ml.docilealligator.infinityforreddit.R; +import ml.docilealligator.infinityforreddit.StreamableVideo; import ml.docilealligator.infinityforreddit.apis.VReddIt; import ml.docilealligator.infinityforreddit.bottomsheetfragments.PlaybackSpeedBottomSheetFragment; import ml.docilealligator.infinityforreddit.customtheme.CustomThemeWrapper; @@ -122,7 +124,9 @@ public class ViewVideoActivity extends AppCompatActivity { public static final String EXTRA_VIDEO_TYPE = "EVT"; public static final String EXTRA_GFYCAT_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 int VIDEO_TYPE_STREAMABLE = 5; public static final int VIDEO_TYPE_V_REDD_IT = 4; public static final int VIDEO_TYPE_DIRECT = 3; public static final int VIDEO_TYPE_REDGIFS = 2; @@ -175,7 +179,6 @@ public class ViewVideoActivity extends AppCompatActivity { private boolean wasPlaying; private boolean isDownloading = false; private boolean isMute = false; - private String postTitle; private boolean isNSFW; private long resumePosition = -1; private int videoType; @@ -183,6 +186,7 @@ public class ViewVideoActivity extends AppCompatActivity { private boolean isHd; private Integer originalOrientation; private int playbackSpeed = 100; + private boolean useBottomAppBar; @Inject @Named("no_oauth") @@ -201,6 +205,10 @@ public class ViewVideoActivity extends AppCompatActivity { Retrofit vReddItRetrofit; @Inject + @Named("streamable") + Retrofit streamableRetrofit; + + @Inject @Named("default") SharedPreferences mSharedPreferences; @@ -281,7 +289,7 @@ public class ViewVideoActivity extends AppCompatActivity { | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN); - boolean useBottomAppBar = mSharedPreferences.getBoolean(SharedPreferencesUtils.USE_BOTTOM_TOOLBAR_IN_MEDIA_VIEWER, false); + useBottomAppBar = mSharedPreferences.getBoolean(SharedPreferencesUtils.USE_BOTTOM_TOOLBAR_IN_MEDIA_VIEWER, false); if (useBottomAppBar) { getSupportActionBar().hide(); bottomAppBar.setVisibility(View.VISIBLE); @@ -345,7 +353,6 @@ public class ViewVideoActivity extends AppCompatActivity { }); Intent intent = getIntent(); - postTitle = intent.getStringExtra(EXTRA_POST_TITLE); isNSFW = intent.getBooleanExtra(EXTRA_IS_NSFW, false); if (savedInstanceState == null) { resumePosition = intent.getLongExtra(EXTRA_PROGRESS_SECONDS, -1); @@ -377,18 +384,8 @@ public class ViewVideoActivity extends AppCompatActivity { } } - - if (postTitle != null) { - if (useBottomAppBar) { - titleTextView.setText(Html.fromHtml(String.format("<font color=\"#FFFFFF\"><small>%s</small></font>", postTitle))); - } else { - setTitle(Html.fromHtml(String.format("<font color=\"#FFFFFF\"><small>%s</small></font>", postTitle))); - } - } else { - if (!useBottomAppBar) { - setTitle(""); - } - } + String postTitle = intent.getStringExtra(EXTRA_POST_TITLE); + setSmallTitle(postTitle); playerControlView.setVisibilityListener(visibility -> { switch (visibility) { @@ -476,7 +473,25 @@ public class ViewVideoActivity extends AppCompatActivity { } setPlaybackSpeed(Integer.parseInt(mSharedPreferences.getString(SharedPreferencesUtils.DEFAULT_PLAYBACK_SPEED, "100"))); - if (videoType == VIDEO_TYPE_V_REDD_IT) { + if (videoType == VIDEO_TYPE_STREAMABLE) { + if (savedInstanceState != null) { + videoDownloadUrl = savedInstanceState.getString(VIDEO_DOWNLOAD_URL_STATE); + } else { + videoDownloadUrl = intent.getStringExtra(EXTRA_VIDEO_DOWNLOAD_URL); + } + + String shortCode = intent.getStringExtra(EXTRA_STREAMABLE_SHORT_CODE); + videoFileName = "Streamable-" + shortCode + ".mp4"; + if (mVideoUri == null) { + loadStreamableVideo(shortCode, savedInstanceState); + } else { + dataSourceFactory = new CacheDataSourceFactory(mSimpleCache, + new DefaultDataSourceFactory(ViewVideoActivity.this, + Util.getUserAgent(ViewVideoActivity.this, "Infinity"))); + player.prepare(new ProgressiveMediaSource.Factory(dataSourceFactory).createMediaSource(mVideoUri)); + preparePlayer(savedInstanceState); + } + } else if (videoType == VIDEO_TYPE_V_REDD_IT) { loadVReddItVideo(savedInstanceState); } else if (videoType == VIDEO_TYPE_GFYCAT || videoType == VIDEO_TYPE_REDGIFS) { if (savedInstanceState != null) { @@ -531,6 +546,20 @@ public class ViewVideoActivity extends AppCompatActivity { } } + private void setSmallTitle(String title) { + if (title != null) { + if (useBottomAppBar) { + titleTextView.setText(Html.fromHtml(String.format("<font color=\"#FFFFFF\"><small>%s</small></font>", title))); + } else { + setTitle(Html.fromHtml(String.format("<font color=\"#FFFFFF\"><small>%s</small></font>", title))); + } + } else { + if (!useBottomAppBar) { + setTitle(""); + } + } + } + private void preparePlayer(Bundle savedInstanceState) { if (mSharedPreferences.getBoolean(SharedPreferencesUtils.LOOP_VIDEO, true)) { player.setRepeatMode(Player.REPEAT_MODE_ALL); @@ -688,7 +717,7 @@ public class ViewVideoActivity extends AppCompatActivity { } loadGfycatOrRedgifsVideo(redgifsRetrofit, gfycatId, savedInstanceState, false); } else { - progressBar.setVisibility(View.INVISIBLE); + progressBar.setVisibility(View.GONE); if (post.getVideoUrl() != null) { mVideoUri = Uri.parse(post.getVideoUrl()); subredditName = post.getSubredditName(); @@ -727,6 +756,34 @@ public class ViewVideoActivity extends AppCompatActivity { }); } + private void loadStreamableVideo(String shortCode, Bundle savedInstanceState) { + progressBar.setVisibility(View.VISIBLE); + FetchStreamableVideo.fetchStreamableVideo(mExecutor, new Handler(), streamableRetrofit, shortCode, + new FetchStreamableVideo.FetchStreamableVideoListener() { + @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; + } + setSmallTitle(streamableVideo.title); + progressBar.setVisibility(View.GONE); + videoDownloadUrl = streamableVideo.mp4 == null ? streamableVideo.mp4Mobile.url : streamableVideo.mp4.url; + mVideoUri = Uri.parse(videoDownloadUrl); + dataSourceFactory = new CacheDataSourceFactory(mSimpleCache, + new DefaultDataSourceFactory(ViewVideoActivity.this, + Util.getUserAgent(ViewVideoActivity.this, "Infinity"))); + preparePlayer(savedInstanceState); + player.prepare(new ProgressiveMediaSource.Factory(dataSourceFactory).createMediaSource(mVideoUri)); + } + + @Override + public void failed() { + Toast.makeText(ViewVideoActivity.this, R.string.fetch_streamable_video_failed, Toast.LENGTH_SHORT).show(); + } + }); + } + @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.view_video_activity, menu); diff --git a/app/src/main/java/ml/docilealligator/infinityforreddit/apis/StreamableAPI.java b/app/src/main/java/ml/docilealligator/infinityforreddit/apis/StreamableAPI.java new file mode 100644 index 00000000..9113c78f --- /dev/null +++ b/app/src/main/java/ml/docilealligator/infinityforreddit/apis/StreamableAPI.java @@ -0,0 +1,10 @@ +package ml.docilealligator.infinityforreddit.apis; + +import retrofit2.Call; +import retrofit2.http.GET; +import retrofit2.http.Path; + +public interface StreamableAPI { + @GET("videos/{shortcode}") + Call<String> getStreamableData(@Path("shortcode") String shortCode); +} diff --git a/app/src/main/java/ml/docilealligator/infinityforreddit/utils/APIUtils.java b/app/src/main/java/ml/docilealligator/infinityforreddit/utils/APIUtils.java index 5078692d..2303b924 100644 --- a/app/src/main/java/ml/docilealligator/infinityforreddit/utils/APIUtils.java +++ b/app/src/main/java/ml/docilealligator/infinityforreddit/utils/APIUtils.java @@ -23,6 +23,7 @@ public class APIUtils { public static final String IMGUR_API_BASE_URI = "https://api.imgur.com/3/"; public static final String PUSHSHIFT_API_BASE_URI = "https://api.pushshift.io/"; public static final String STRAPI_BASE_URI = "https://strapi.reddit.com"; + public static final String STREAMABLE_API_BASE_URI = "https://api.streamable.com"; public static final String CLIENT_ID_KEY = "client_id"; public static final String CLIENT_ID = "NOe2iKrPPzwscA"; diff --git a/app/src/main/java/ml/docilealligator/infinityforreddit/utils/JSONUtils.java b/app/src/main/java/ml/docilealligator/infinityforreddit/utils/JSONUtils.java index 19ea43cb..42e13ce1 100644 --- a/app/src/main/java/ml/docilealligator/infinityforreddit/utils/JSONUtils.java +++ b/app/src/main/java/ml/docilealligator/infinityforreddit/utils/JSONUtils.java @@ -173,4 +173,6 @@ public class JSONUtils { public static final String CONTENT_MD_KEY = "content_md"; public static final String CAPTION_KEY = "caption"; public static final String CAPTION_URL_KEY = "outbound_url"; + public static final String FILES_KEY = "files"; + public static final String MP4_MOBILE_KEY = "mp4-mobile"; } |