Support viewing gfycat videos in app.

This commit is contained in:
Alex Ning 2020-05-28 22:16:07 +08:00
parent 8d715956cb
commit 066956971a
11 changed files with 264 additions and 74 deletions

View File

@ -42,6 +42,7 @@ dependencies {
implementation 'com.google.android.exoplayer:exoplayer-core:2.10.4'
implementation 'com.google.android.exoplayer:exoplayer-hls:2.10.4'
implementation 'com.google.android.exoplayer:exoplayer-dash:2.10.4'
implementation 'com.google.android.exoplayer:exoplayer-ui:2.10.4'
implementation 'com.alexvasilkov:gesture-views:2.6.0'
implementation 'com.github.bumptech.glide:glide:4.10.0'
@ -64,7 +65,7 @@ dependencies {
implementation 'io.noties.markwon:simple-ext:4.3.1'
implementation 'io.noties.markwon:recycler-table:4.3.1'
implementation 'pl.droidsonroids.gif:android-gif-drawable:1.2.19'
implementation 'com.github.Ferfalk:SimpleSearchView:0.1.5'
implementation 'com.github.Ferfalk:SimpleSearchView:0.1.4'
implementation 'org.greenrobot:eventbus:3.2.0'
implementation 'com.libRG:customtextview:2.4'
implementation 'com.github.Deishelon:RoundedBottomSheet:1.0.1'

View File

@ -21,10 +21,11 @@
android:theme="@style/AppTheme"
android:usesCleartextTraffic="true"
tools:replace="android:label">
<activity android:name=".Activity.ReportActivity"
<activity
android:name=".Activity.ReportActivity"
android:label="@string/report_activity_label"
android:parentActivityName=".Activity.MainActivity"
android:theme="@style/AppTheme.NoActionBar"/>
android:theme="@style/AppTheme.NoActionBar" />
<activity
android:name=".Activity.SelectedSubredditsActivity"
android:label="@string/selected_subeddits_activity_label"

View File

@ -38,6 +38,7 @@ public class LinkResolverActivity extends AppCompatActivity {
private static final String MULTIREDDIT_PATTERN = "/user/\\w+/m/\\w+/?";
private static final String MULTIREDDIT_PATTERN_2 = "/[rR]/(\\w+\\+?)+/?";
private static final String REDD_IT_POST_PATTERN = "/\\w+/?";
private static final String GFYCAT_PATTERN = "/[\\w-]+$";
@Inject
@Named("default")
@ -78,78 +79,89 @@ public class LinkResolverActivity extends AppCompatActivity {
String authority = uri.getAuthority();
List<String> segments = uri.getPathSegments();
if (authority != null && (authority.contains("reddit.com") || authority.contains("redd.it") || authority.contains("reddit.app"))) {
if (authority.equals("reddit.app.link") && path.isEmpty()) {
String redirect = uri.getQueryParameter("$og_redirect");
handleUri(Uri.parse(redirect));
} else if (path.isEmpty()) {
Intent intent = new Intent(this, MainActivity.class);
startActivity(intent);
} else if (path.matches(POST_PATTERN)) {
int commentsIndex = segments.lastIndexOf("comments");
if (commentsIndex >= 0 && commentsIndex < segments.size() - 1) {
Intent intent = new Intent(this, ViewPostDetailActivity.class);
intent.putExtra(ViewPostDetailActivity.EXTRA_POST_ID, segments.get(commentsIndex + 1));
intent.putExtra(ViewPostDetailActivity.EXTRA_MESSAGE_FULLNAME, messageFullname);
intent.putExtra(ViewPostDetailActivity.EXTRA_NEW_ACCOUNT_NAME, newAccountName);
startActivity(intent);
} else {
deepLinkError(uri);
}
} else if (path.matches(COMMENT_PATTERN)) {
int commentsIndex = segments.lastIndexOf("comments");
if (commentsIndex >= 0 && commentsIndex < segments.size() - 1) {
Intent intent = new Intent(this, ViewPostDetailActivity.class);
intent.putExtra(ViewPostDetailActivity.EXTRA_POST_ID, segments.get(commentsIndex + 1));
intent.putExtra(ViewPostDetailActivity.EXTRA_SINGLE_COMMENT_ID, segments.get(segments.size() - 1));
intent.putExtra(ViewPostDetailActivity.EXTRA_MESSAGE_FULLNAME, messageFullname);
intent.putExtra(ViewPostDetailActivity.EXTRA_NEW_ACCOUNT_NAME, newAccountName);
startActivity(intent);
} else {
deepLinkError(uri);
}
} else if (path.matches(SUBREDDIT_PATTERN)) {
String subredditName = path.substring(3);
if (subredditName.equals("popular") || subredditName.equals("all")) {
if (authority != null) {
if (authority.contains("reddit.com") || authority.contains("redd.it") || authority.contains("reddit.app")) {
if (authority.equals("reddit.app.link") && path.isEmpty()) {
String redirect = uri.getQueryParameter("$og_redirect");
handleUri(Uri.parse(redirect));
} else if (path.isEmpty()) {
Intent intent = new Intent(this, MainActivity.class);
intent.putExtra(MainActivity.EXTRA_POST_TYPE, subredditName);
intent.putExtra(MainActivity.EXTRA_MESSSAGE_FULLNAME, messageFullname);
intent.putExtra(MainActivity.EXTRA_NEW_ACCOUNT_NAME, newAccountName);
startActivity(intent);
} else {
} else if (path.matches(POST_PATTERN)) {
int commentsIndex = segments.lastIndexOf("comments");
if (commentsIndex >= 0 && commentsIndex < segments.size() - 1) {
Intent intent = new Intent(this, ViewPostDetailActivity.class);
intent.putExtra(ViewPostDetailActivity.EXTRA_POST_ID, segments.get(commentsIndex + 1));
intent.putExtra(ViewPostDetailActivity.EXTRA_MESSAGE_FULLNAME, messageFullname);
intent.putExtra(ViewPostDetailActivity.EXTRA_NEW_ACCOUNT_NAME, newAccountName);
startActivity(intent);
} else {
deepLinkError(uri);
}
} else if (path.matches(COMMENT_PATTERN)) {
int commentsIndex = segments.lastIndexOf("comments");
if (commentsIndex >= 0 && commentsIndex < segments.size() - 1) {
Intent intent = new Intent(this, ViewPostDetailActivity.class);
intent.putExtra(ViewPostDetailActivity.EXTRA_POST_ID, segments.get(commentsIndex + 1));
intent.putExtra(ViewPostDetailActivity.EXTRA_SINGLE_COMMENT_ID, segments.get(segments.size() - 1));
intent.putExtra(ViewPostDetailActivity.EXTRA_MESSAGE_FULLNAME, messageFullname);
intent.putExtra(ViewPostDetailActivity.EXTRA_NEW_ACCOUNT_NAME, newAccountName);
startActivity(intent);
} else {
deepLinkError(uri);
}
} else if (path.matches(SUBREDDIT_PATTERN)) {
String subredditName = path.substring(3);
if (subredditName.equals("popular") || subredditName.equals("all")) {
Intent intent = new Intent(this, MainActivity.class);
intent.putExtra(MainActivity.EXTRA_POST_TYPE, subredditName);
intent.putExtra(MainActivity.EXTRA_MESSSAGE_FULLNAME, messageFullname);
intent.putExtra(MainActivity.EXTRA_NEW_ACCOUNT_NAME, newAccountName);
startActivity(intent);
} else {
Intent intent = new Intent(this, ViewSubredditDetailActivity.class);
intent.putExtra(ViewSubredditDetailActivity.EXTRA_SUBREDDIT_NAME_KEY, path.substring(3));
intent.putExtra(ViewSubredditDetailActivity.EXTRA_MESSAGE_FULLNAME, messageFullname);
intent.putExtra(ViewSubredditDetailActivity.EXTRA_NEW_ACCOUNT_NAME, newAccountName);
startActivity(intent);
}
} else if (path.matches(USER_PATTERN)) {
Intent intent = new Intent(this, ViewUserDetailActivity.class);
intent.putExtra(ViewUserDetailActivity.EXTRA_USER_NAME_KEY, segments.get(1));
intent.putExtra(ViewUserDetailActivity.EXTRA_MESSAGE_FULLNAME, messageFullname);
intent.putExtra(ViewUserDetailActivity.EXTRA_NEW_ACCOUNT_NAME, newAccountName);
startActivity(intent);
} else if (path.matches(SIDEBAR_PATTERN)) {
Intent intent = new Intent(this, ViewSidebarActivity.class);
intent.putExtra(ViewSidebarActivity.EXTRA_SUBREDDIT_NAME, path.substring(3, path.length() - 14));
startActivity(intent);
} else if (path.matches(MULTIREDDIT_PATTERN)) {
Intent intent = new Intent(this, ViewMultiRedditDetailActivity.class);
intent.putExtra(ViewMultiRedditDetailActivity.EXTRA_MULTIREDDIT_PATH, path);
startActivity(intent);
} else if (path.matches(MULTIREDDIT_PATTERN_2)) {
String subredditName = path.substring(3);
Intent intent = new Intent(this, ViewSubredditDetailActivity.class);
intent.putExtra(ViewSubredditDetailActivity.EXTRA_SUBREDDIT_NAME_KEY, path.substring(3));
intent.putExtra(ViewSubredditDetailActivity.EXTRA_SUBREDDIT_NAME_KEY, subredditName);
intent.putExtra(ViewSubredditDetailActivity.EXTRA_MESSAGE_FULLNAME, messageFullname);
intent.putExtra(ViewSubredditDetailActivity.EXTRA_NEW_ACCOUNT_NAME, newAccountName);
startActivity(intent);
} else if (authority.equals("redd.it") && path.matches(REDD_IT_POST_PATTERN)) {
Intent intent = new Intent(this, ViewPostDetailActivity.class);
intent.putExtra(ViewPostDetailActivity.EXTRA_POST_ID, path.substring(1));
startActivity(intent);
} else {
deepLinkError(uri);
}
} else if (authority.contains("gfycat.com")) {
if (path.matches(GFYCAT_PATTERN)) {
Intent intent = new Intent(this, ViewVideoActivity.class);
intent.putExtra(ViewVideoActivity.EXTRA_GFYCAT_ID, path.substring(1));
intent.putExtra(ViewVideoActivity.EXTRA_VIDEO_TYPE, ViewVideoActivity.VIDEO_TYPE_GFYCAT);
startActivity(intent);
} else {
deepLinkError(uri);
}
} else if (path.matches(USER_PATTERN)) {
Intent intent = new Intent(this, ViewUserDetailActivity.class);
intent.putExtra(ViewUserDetailActivity.EXTRA_USER_NAME_KEY, segments.get(1));
intent.putExtra(ViewUserDetailActivity.EXTRA_MESSAGE_FULLNAME, messageFullname);
intent.putExtra(ViewUserDetailActivity.EXTRA_NEW_ACCOUNT_NAME, newAccountName);
startActivity(intent);
} else if (path.matches(SIDEBAR_PATTERN)) {
Intent intent = new Intent(this, ViewSidebarActivity.class);
intent.putExtra(ViewSidebarActivity.EXTRA_SUBREDDIT_NAME, path.substring(3, path.length() - 14));
startActivity(intent);
} else if (path.matches(MULTIREDDIT_PATTERN)) {
Intent intent = new Intent(this, ViewMultiRedditDetailActivity.class);
intent.putExtra(ViewMultiRedditDetailActivity.EXTRA_MULTIREDDIT_PATH, path);
startActivity(intent);
} else if (path.matches(MULTIREDDIT_PATTERN_2)) {
String subredditName = path.substring(3);
Intent intent = new Intent(this, ViewSubredditDetailActivity.class);
intent.putExtra(ViewSubredditDetailActivity.EXTRA_SUBREDDIT_NAME_KEY, subredditName);
intent.putExtra(ViewSubredditDetailActivity.EXTRA_MESSAGE_FULLNAME, messageFullname);
intent.putExtra(ViewSubredditDetailActivity.EXTRA_NEW_ACCOUNT_NAME, newAccountName);
startActivity(intent);
} else if (authority.equals("redd.it") && path.matches(REDD_IT_POST_PATTERN)) {
Intent intent = new Intent(this, ViewPostDetailActivity.class);
intent.putExtra(ViewPostDetailActivity.EXTRA_POST_ID, path.substring(1));
startActivity(intent);
} else {
deepLinkError(uri);
}
} else {
deepLinkError(uri);

View File

@ -40,7 +40,9 @@ import com.github.pwittchen.swipe.library.rx2.Swipe;
import com.google.android.exoplayer2.ExoPlayerFactory;
import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.SimpleExoPlayer;
import com.google.android.exoplayer2.source.ProgressiveMediaSource;
import com.google.android.exoplayer2.source.TrackGroupArray;
import com.google.android.exoplayer2.source.dash.DashMediaSource;
import com.google.android.exoplayer2.source.hls.HlsMediaSource;
import com.google.android.exoplayer2.trackselection.AdaptiveTrackSelection;
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector;
@ -49,6 +51,7 @@ import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
import com.google.android.exoplayer2.trackselection.TrackSelector;
import com.google.android.exoplayer2.ui.PlayerView;
import com.google.android.exoplayer2.upstream.DataSource;
import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory;
import com.google.android.exoplayer2.upstream.DefaultHttpDataSourceFactory;
import com.google.android.exoplayer2.util.Util;
@ -59,9 +62,11 @@ import javax.inject.Named;
import butterknife.BindView;
import butterknife.ButterKnife;
import ml.docilealligator.infinityforreddit.FetchGfycatVideoLinks;
import ml.docilealligator.infinityforreddit.Infinity;
import ml.docilealligator.infinityforreddit.R;
import ml.docilealligator.infinityforreddit.Utils.SharedPreferencesUtils;
import retrofit2.Retrofit;
public class ViewVideoActivity extends AppCompatActivity {
@ -70,8 +75,14 @@ public class ViewVideoActivity extends AppCompatActivity {
public static final String EXTRA_ID = "EI";
public static final String EXTRA_POST_TITLE = "EPT";
public static final String EXTRA_PROGRESS_SECONDS = "EPS";
public static final String EXTRA_VIDEO_TYPE = "EVT";
public static final String EXTRA_GFYCAT_ID = "EGI";
public static final int VIDEO_TYPE_GFYCAT = 1;
private static final int VIDEO_TYPE_NORMAL = 0;
private static final int PERMISSION_REQUEST_WRITE_EXTERNAL_STORAGE = 0;
private static final String IS_MUTE_STATE = "IMS";
private static final String VIDEO_DOWNLOAD_URL_STATE = "VDUS";
private static final String VIDEO_URI_STATE = "VUS";
@BindView(R.id.relative_layout_view_video_activity)
RelativeLayout relativeLayout;
@BindView(R.id.player_view_view_video_activity)
@ -96,6 +107,10 @@ public class ViewVideoActivity extends AppCompatActivity {
private String postTitle;
private long resumePosition = -1;
@Inject
@Named("gfycat")
Retrofit gfycatRetrofit;
@Inject
@Named("default")
SharedPreferences mSharedPreferences;
@ -133,8 +148,6 @@ public class ViewVideoActivity extends AppCompatActivity {
Intent intent = getIntent();
mVideoUri = intent.getData();
videoDownloadUrl = intent.getStringExtra(EXTRA_VIDEO_DOWNLOAD_URL);
videoFileName = intent.getStringExtra(EXTRA_SUBREDDIT) + "-" + intent.getStringExtra(EXTRA_ID) + ".mp4";
postTitle = intent.getStringExtra(EXTRA_POST_TITLE);
if (savedInstanceState == null) {
resumePosition = intent.getLongExtra(EXTRA_PROGRESS_SECONDS, -1);
@ -319,11 +332,57 @@ public class ViewVideoActivity extends AppCompatActivity {
TrackSelector trackSelector = new DefaultTrackSelector(videoTrackSelectionFactory);
player = ExoPlayerFactory.newSimpleInstance(this, trackSelector);
videoPlayerView.setPlayer(player);
// Produces DataSource instances through which media data is loaded.
dataSourceFactory = new DefaultHttpDataSourceFactory(Util.getUserAgent(this, "Infinity"));
// Prepare the player with the source.
player.prepare(new HlsMediaSource.Factory(dataSourceFactory).createMediaSource(mVideoUri));
if (getIntent().getIntExtra(EXTRA_VIDEO_TYPE, VIDEO_TYPE_NORMAL) == VIDEO_TYPE_GFYCAT) {
if (savedInstanceState != null) {
String videoUrl = savedInstanceState.getString(VIDEO_URI_STATE);
if (videoUrl != null) {
mVideoUri = Uri.parse(videoUrl);
videoDownloadUrl = savedInstanceState.getString(VIDEO_DOWNLOAD_URL_STATE);
}
}
if (mVideoUri == null) {
String gfycatId = intent.getStringExtra(EXTRA_GFYCAT_ID);
if (gfycatId != null && gfycatId.contains("-")) {
gfycatId = gfycatId.substring(0, gfycatId.indexOf('-'));
}
videoFileName = "Gfycat-" + gfycatId + ".mp4";
FetchGfycatVideoLinks.fetchGfycatVideoLinks(gfycatRetrofit, gfycatId,
new FetchGfycatVideoLinks.FetchGfycatVideoLinksListener() {
@Override
public void success(String webm, String mp4) {
mVideoUri = Uri.parse(webm);
videoDownloadUrl = mp4;
dataSourceFactory = new DefaultDataSourceFactory(ViewVideoActivity.this,
Util.getUserAgent(ViewVideoActivity.this, "Infinity"));
player.prepare(new ProgressiveMediaSource.Factory(dataSourceFactory).createMediaSource(mVideoUri));
preparePlayer(savedInstanceState);
}
@Override
public void failed() {
Toast.makeText(ViewVideoActivity.this, R.string.fetch_gfycat_video_failed, Toast.LENGTH_SHORT).show();
}
});
} else {
dataSourceFactory = new DefaultDataSourceFactory(ViewVideoActivity.this,
Util.getUserAgent(ViewVideoActivity.this, "Infinity"));
player.prepare(new DashMediaSource.Factory(dataSourceFactory).createMediaSource(mVideoUri));
preparePlayer(savedInstanceState);
}
} else {
videoDownloadUrl = intent.getStringExtra(EXTRA_VIDEO_DOWNLOAD_URL);
videoFileName = intent.getStringExtra(EXTRA_SUBREDDIT) + "-" + intent.getStringExtra(EXTRA_ID) + ".mp4";
// Produces DataSource instances through which media data is loaded.
dataSourceFactory = new DefaultHttpDataSourceFactory(Util.getUserAgent(this, "Infinity"));
// Prepare the player with the source.
player.prepare(new HlsMediaSource.Factory(dataSourceFactory).createMediaSource(mVideoUri));
}
preparePlayer(savedInstanceState);
}
private void preparePlayer(Bundle savedInstanceState) {
player.setRepeatMode(Player.REPEAT_MODE_ALL);
if (resumePosition > 0) {
player.seekTo(resumePosition);
@ -405,6 +464,11 @@ public class ViewVideoActivity extends AppCompatActivity {
finish();
return true;
case R.id.action_download_view_video_activity:
if (videoDownloadUrl == null) {
Toast.makeText(this, R.string.fetching_video_info_please_wait, Toast.LENGTH_SHORT).show();
return true;
}
isDownloading = true;
if (Build.VERSION.SDK_INT >= 23) {
if (ContextCompat.checkSelfPermission(this,
@ -512,5 +576,9 @@ public class ViewVideoActivity extends AppCompatActivity {
protected void onSaveInstanceState(@NonNull Bundle outState) {
super.onSaveInstanceState(outState);
outState.putBoolean(IS_MUTE_STATE, isMute);
if (mVideoUri != null) {
outState.putString(VIDEO_URI_STATE, mVideoUri.toString());
outState.putString(VIDEO_DOWNLOAD_URL_STATE, videoDownloadUrl);
}
}
}

View File

@ -90,6 +90,16 @@ class AppModule {
.build();
}
@Provides
@Named("gfycat")
@Singleton
Retrofit provideGfycatRetrofit() {
return new Retrofit.Builder()
.baseUrl(RedditUtils.GFYCAT_API_BASE_URI)
.addConverterFactory(ScalarsConverterFactory.create())
.build();
}
@Provides
@Singleton
OkHttpClient provideOkHttpClient(@Named("no_oauth") Retrofit retrofit, RedditDataRoomDatabase accountRoomDatabase) {

View File

@ -0,0 +1,78 @@
package ml.docilealligator.infinityforreddit;
import android.os.AsyncTask;
import androidx.annotation.NonNull;
import org.json.JSONException;
import org.json.JSONObject;
import ml.docilealligator.infinityforreddit.Utils.JSONUtils;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
import retrofit2.Retrofit;
public class FetchGfycatVideoLinks {
public interface FetchGfycatVideoLinksListener {
void success(String webm, String mp4);
void failed();
}
public static void fetchGfycatVideoLinks(Retrofit gfycatRetrofit, String gfycatId,
FetchGfycatVideoLinksListener fetchGfycatVideoLinksListener) {
gfycatRetrofit.create(GfycatAPI.class).getSubredditData(gfycatId).enqueue(new Callback<String>() {
@Override
public void onResponse(@NonNull Call<String> call, @NonNull Response<String> response) {
if (response.isSuccessful()) {
new ParseGfycatVideoLinksAsyncTask(response.body(), fetchGfycatVideoLinksListener).execute();
} else {
fetchGfycatVideoLinksListener.failed();
}
}
@Override
public void onFailure(@NonNull Call<String> call, @NonNull Throwable t) {
fetchGfycatVideoLinksListener.failed();
}
});
}
private static class ParseGfycatVideoLinksAsyncTask extends AsyncTask<Void, Void, Void> {
private String response;
private String webm;
private String mp4;
private boolean parseFailed = false;
private FetchGfycatVideoLinksListener fetchGfycatVideoLinksListener;
ParseGfycatVideoLinksAsyncTask(String response, FetchGfycatVideoLinksListener fetchGfycatVideoLinksListener) {
this.response = response;
this.fetchGfycatVideoLinksListener = fetchGfycatVideoLinksListener;
}
@Override
protected Void doInBackground(Void... voids) {
try {
JSONObject jsonObject = new JSONObject(response);
webm = jsonObject.getJSONObject(JSONUtils.GFY_ITEM_KEY).getString(JSONUtils.WEBM_URL_KEY);
mp4 = jsonObject.getJSONObject(JSONUtils.GFY_ITEM_KEY).getString(JSONUtils.MP4_URL_KEY);
} catch (JSONException e) {
e.printStackTrace();
parseFailed = true;
}
return null;
}
@Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
if (parseFailed) {
fetchGfycatVideoLinksListener.failed();
} else {
fetchGfycatVideoLinksListener.success(webm, mp4);
}
}
}
}

View File

@ -0,0 +1,10 @@
package ml.docilealligator.infinityforreddit;
import retrofit2.Call;
import retrofit2.http.GET;
import retrofit2.http.Path;
public interface GfycatAPI {
@GET("{gfyid}")
Call<String> getSubredditData(@Path("gfyid") String gfyId);
}

View File

@ -106,4 +106,7 @@ public class JSONUtils {
public static final String PATH_KEY = "path";
public static final String ALL_AWARDINGS_KEY = "all_awardings";
public static final String RESIZED_ICONS_KEY = "resized_icons";
public static final String GFY_ITEM_KEY = "gfyItem";
public static final String MP4_URL_KEY = "mp4Url";
public static final String WEBM_URL_KEY = "webmUrl";
}

View File

@ -18,6 +18,7 @@ public class RedditUtils {
public static final String API_BASE_URI = "https://www.reddit.com";
public static final String API_UPLOAD_MEDIA_URI = "https://reddit-uploaded-media.s3-accelerate.amazonaws.com";
public static final String API_UPLOAD_VIDEO_URI = "https://reddit-uploaded-video.s3-accelerate.amazonaws.com";
public static final String GFYCAT_API_BASE_URI = "https://api.gfycat.com/v1/gfycats/";
public static final String CLIENT_ID_KEY = "client_id";
public static final String CLIENT_ID = "";

View File

@ -37,4 +37,7 @@
<attr name="backgroundColor" format="color"/>
<attr name="cardViewBackgroundColor" format="color"/>
</declare-styleable>
<attr name="backIconAlpha"/>
<attr name="iconsAlpha"/>
</resources>

View File

@ -731,4 +731,7 @@
<string name="home">Home</string>
<string name="popular">Popular</string>
<string name="fetch_gfycat_video_failed">Fetch Gfycat video failed</string>
<string name="fetching_video_info_please_wait">Fetching video info. Please wait.</string>
</resources>