Support Streamable.

This commit is contained in:
Alex Ning 2021-12-09 21:29:35 +08:00
parent 35cbd24318
commit 1abc703d7e
9 changed files with 197 additions and 19 deletions

View File

@ -167,6 +167,16 @@ class AppModule {
.build();
}
@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

View File

@ -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;
}
}
}

View File

@ -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;
}
}
}

View File

@ -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);
}

View File

@ -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")
@ -200,6 +204,10 @@ public class ViewVideoActivity extends AppCompatActivity {
@Named("vReddIt")
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);

View File

@ -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);
}

View File

@ -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";

View File

@ -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";
}

View File

@ -988,6 +988,7 @@
<string name="fetch_gfycat_video_failed">Fetch Gfycat video failed</string>
<string name="fetch_redgifs_video_failed">Fetch Redgifs video failed</string>
<string name="fetching_video_info_please_wait">Fetching video info. Please wait.</string>
<string name="fetch_streamable_video_failed">Fetch Streamable video failed</string>
<string name="error_fetching_imgur_media">Cannot load images</string>