Manage Peertube videos

This commit is contained in:
Thomas 2022-06-28 16:28:11 +02:00
parent fc4cbc5334
commit a48f997ae3
9 changed files with 123 additions and 27 deletions

View file

@ -125,7 +125,7 @@ public class MediaActivity extends BaseActivity implements OnDownloadInterface {
ScreenSlidePagerAdapter mPagerAdapter = new ScreenSlidePagerAdapter(MediaActivity.this); ScreenSlidePagerAdapter mPagerAdapter = new ScreenSlidePagerAdapter(MediaActivity.this);
binding.mediaViewpager.setAdapter(mPagerAdapter); binding.mediaViewpager.setAdapter(mPagerAdapter);
binding.mediaViewpager.setSaveEnabled(false);
binding.mediaViewpager.setCurrentItem(mediaPosition - 1); binding.mediaViewpager.setCurrentItem(mediaPosition - 1);
binding.haulerView.setOnDragDismissedListener(dragDirection -> ActivityCompat.finishAfterTransition(MediaActivity.this)); binding.haulerView.setOnDragDismissedListener(dragDirection -> ActivityCompat.finishAfterTransition(MediaActivity.this));
registerReceiver(onDownloadComplete, new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE)); registerReceiver(onDownloadComplete, new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE));

View file

@ -83,6 +83,7 @@ public class SearchResultTabActivity extends BaseActivity {
binding.searchTabLayout.setTabIconTint(ThemeHelper.getColorStateList(SearchResultTabActivity.this)); binding.searchTabLayout.setTabIconTint(ThemeHelper.getColorStateList(SearchResultTabActivity.this));
ScreenSlidePagerAdapter mPagerAdapter = new ScreenSlidePagerAdapter(SearchResultTabActivity.this); ScreenSlidePagerAdapter mPagerAdapter = new ScreenSlidePagerAdapter(SearchResultTabActivity.this);
binding.searchViewpager.setAdapter(mPagerAdapter); binding.searchViewpager.setAdapter(mPagerAdapter);
binding.searchViewpager.setSaveEnabled(false);
binding.searchViewpager.setOffscreenPageLimit(3); binding.searchViewpager.setOffscreenPageLimit(3);
new TabLayoutMediator(binding.searchTabLayout, binding.searchViewpager, new TabLayoutMediator(binding.searchTabLayout, binding.searchViewpager,
(tab, position) -> { (tab, position) -> {

View file

@ -224,4 +224,8 @@ public interface MastodonTimelinesService {
@Query("count") int count @Query("count") int count
); );
@GET("api/v1/videos/{id}")
Call<PeertubeVideo.Video> getPeertubeVideo(
@Path("id") String id
);
} }

View file

@ -44,4 +44,6 @@ public class Attachment implements Serializable {
@SerializedName("local_path") @SerializedName("local_path")
public String local_path; public String local_path;
public String peertubeHost = null;
public String peertubeId = null;
} }

View file

@ -56,9 +56,11 @@ public class PeertubeVideo implements Serializable {
status.account = account; status.account = account;
List<Attachment> attachmentList = new ArrayList<>(); List<Attachment> attachmentList = new ArrayList<>();
Attachment attachment = new Attachment(); Attachment attachment = new Attachment();
attachment.type = "video/mp4"; attachment.type = "video";
attachment.url = "https://" + peertubeVideo.account.host + peertubeVideo.embedPath; attachment.url = "https://" + peertubeVideo.account.host + peertubeVideo.embedPath;
attachment.preview_url = "https://" + peertubeVideo.account.host + peertubeVideo.thumbnailPath; attachment.preview_url = "https://" + peertubeVideo.account.host + peertubeVideo.thumbnailPath;
attachment.peertubeId = peertubeVideo.uuid;
attachment.peertubeHost = peertubeVideo.account.host;
attachmentList.add(attachment); attachmentList.add(attachment);
status.media_attachments = attachmentList; status.media_attachments = attachmentList;
return status; return status;
@ -111,11 +113,33 @@ public class PeertubeVideo implements Serializable {
public Date updatedAt; public Date updatedAt;
@SerializedName("uuid") @SerializedName("uuid")
public String uuid; public String uuid;
@SerializedName("files")
public List<File> files;
@SerializedName("views") @SerializedName("views")
public int views; public int views;
} }
public class File implements Serializable {
@SerializedName("fileDownloadUrl")
public String fileDownloadUrl;
@SerializedName("fileUrl")
public String fileUrl;
@SerializedName("fps")
public int fps;
@SerializedName("magnetUri")
public String magnetUri;
@SerializedName("metadataUrl")
public String metadataUrl;
@SerializedName("resolution")
public Item resolutions;
@SerializedName("size")
public long size;
@SerializedName("torrentDownloadUrl")
public String torrentDownloadUrl;
@SerializedName("torrentUrl")
public String torrentUrl;
}
public static class PeertubeAccount implements Serializable { public static class PeertubeAccount implements Serializable {
@SerializedName("avatar") @SerializedName("avatar")
public Avatar avatar; public Avatar avatar;

View file

@ -280,6 +280,7 @@ public class Helper {
public static final Pattern libredditPattern = Pattern.compile("(www\\.|m\\.)?(reddit\\.com|preview\\.redd\\.it|i\\.redd\\.it|redd\\.it)/(((?!([\"'<])).)*)"); public static final Pattern libredditPattern = Pattern.compile("(www\\.|m\\.)?(reddit\\.com|preview\\.redd\\.it|i\\.redd\\.it|redd\\.it)/(((?!([\"'<])).)*)");
public static final Pattern ouichesPattern = Pattern.compile("https?://ouich\\.es/tag/(\\w+)"); public static final Pattern ouichesPattern = Pattern.compile("https?://ouich\\.es/tag/(\\w+)");
public static final Pattern xmppPattern = Pattern.compile("xmpp:[-a-zA-Z0-9+$&@#/%?=~_|!:,.;]*[-a-zA-Z0-9+&@#/%=~_|]"); public static final Pattern xmppPattern = Pattern.compile("xmpp:[-a-zA-Z0-9+$&@#/%?=~_|!:,.;]*[-a-zA-Z0-9+&@#/%=~_|]");
public static final Pattern peertubePattern = Pattern.compile("(https?://([\\da-z.-]+\\.[a-z.]{2,10}))/videos/watch/(\\w{8}-\\w{4}-\\w{4}-\\w{4}-\\w{12})$");
public static final Pattern mediumPattern = Pattern.compile("([\\w@-]*)?\\.?medium.com/@?([/\\w-]+)"); public static final Pattern mediumPattern = Pattern.compile("([\\w@-]*)?\\.?medium.com/@?([/\\w-]+)");
public static final Pattern wikipediaPattern = Pattern.compile("([\\w_-]+)\\.wikipedia.org/(((?!([\"'<])).)*)"); public static final Pattern wikipediaPattern = Pattern.compile("([\\w_-]+)\\.wikipedia.org/(((?!([\"'<])).)*)");
public static final Pattern codePattern = Pattern.compile("code=([\\w-]+)"); public static final Pattern codePattern = Pattern.compile("code=([\\w-]+)");

View file

@ -433,6 +433,24 @@ public class StatusAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
holder.binding.statusContent.setTextSize(TypedValue.COMPLEX_UNIT_SP, 14); holder.binding.statusContent.setTextSize(TypedValue.COMPLEX_UNIT_SP, 14);
holder.binding.spoiler.setTextSize(TypedValue.COMPLEX_UNIT_SP, 14); holder.binding.spoiler.setTextSize(TypedValue.COMPLEX_UNIT_SP, 14);
} }
//If the message contain a link to peertube and no media was added, we add it
if (statusToDeal.card != null && statusToDeal.card.url != null && (statusToDeal.media_attachments == null || statusToDeal.media_attachments.size() == 0)) {
Matcher matcherLink = Helper.peertubePattern.matcher(statusToDeal.card.url);
if (matcherLink.find()) { //Peertubee video
List<Attachment> attachmentList = new ArrayList<>();
Attachment attachment = new Attachment();
attachment.type = "video";
attachment.url = matcherLink.group(0);
attachment.preview_url = statusToDeal.card.image;
attachment.peertubeHost = matcherLink.group(2);
attachment.peertubeId = matcherLink.group(3);
attachmentList.add(attachment);
statusToDeal.media_attachments = attachmentList;
adapter.notifyItemChanged(getPositionAsync(notificationList, statusList, statusToDeal));
}
}
if (status.card != null && (display_card || status.isFocused)) { if (status.card != null && (display_card || status.isFocused)) {
if (status.card.width > status.card.height) { if (status.card.width > status.card.height) {
holder.binding.cardImageHorizontal.setVisibility(View.VISIBLE); holder.binding.cardImageHorizontal.setVisibility(View.VISIBLE);

View file

@ -31,6 +31,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.core.app.ActivityCompat; import androidx.core.app.ActivityCompat;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider;
import androidx.preference.PreferenceManager; import androidx.preference.PreferenceManager;
import com.bumptech.glide.Glide; import com.bumptech.glide.Glide;
@ -51,6 +52,7 @@ import app.fedilab.android.client.entities.api.Attachment;
import app.fedilab.android.databinding.FragmentSlideMediaBinding; import app.fedilab.android.databinding.FragmentSlideMediaBinding;
import app.fedilab.android.helper.CacheDataSourceFactory; import app.fedilab.android.helper.CacheDataSourceFactory;
import app.fedilab.android.helper.Helper; import app.fedilab.android.helper.Helper;
import app.fedilab.android.viewmodel.mastodon.TimelinesVM;
import app.fedilab.android.webview.CustomWebview; import app.fedilab.android.webview.CustomWebview;
import app.fedilab.android.webview.FedilabWebChromeClient; import app.fedilab.android.webview.FedilabWebChromeClient;
import app.fedilab.android.webview.FedilabWebViewClient; import app.fedilab.android.webview.FedilabWebViewClient;
@ -198,32 +200,18 @@ public class FragmentMedia extends Fragment {
case "video": case "video":
case "audio": case "audio":
case "gifv": case "gifv":
binding.pbarInf.setIndeterminate(false); if (attachment.peertubeId != null) {
binding.pbarInf.setScaleY(3f); //It's a peertube video, we are fetching data
binding.mediaVideo.setVisibility(View.VISIBLE); TimelinesVM timelinesVM = new ViewModelProvider(requireActivity()).get(TimelinesVM.class);
Uri uri = Uri.parse(url); String finalType = type;
timelinesVM.getPeertubeVideo(attachment.peertubeHost, attachment.peertubeId).observe(requireActivity(), video -> {
String userAgent = sharedpreferences.getString(getString(R.string.SET_CUSTOM_USER_AGENT), Helper.USER_AGENT); if (video != null && video.files != null && video.files.size() > 0) {
int video_cache = sharedpreferences.getInt(getString(R.string.SET_VIDEO_CACHE), Helper.DEFAULT_VIDEO_CACHE_MB); loadVideo(video.files.get(0).fileUrl, finalType);
ProgressiveMediaSource videoSource; }
if (video_cache == 0) { });
DataSource.Factory dataSourceFactory = new DefaultDataSourceFactory(requireActivity(),
Util.getUserAgent(requireActivity(), userAgent), null);
videoSource = new ProgressiveMediaSource.Factory(dataSourceFactory)
.createMediaSource(uri);
} else { } else {
CacheDataSourceFactory cacheDataSourceFactory = new CacheDataSourceFactory(requireActivity()); loadVideo(url, type);
videoSource = new ProgressiveMediaSource.Factory(cacheDataSourceFactory)
.createMediaSource(uri);
} }
player = new SimpleExoPlayer.Builder(requireActivity()).build();
if (type.equalsIgnoreCase("gifv"))
player.setRepeatMode(Player.REPEAT_MODE_ONE);
binding.mediaVideo.setPlayer(player);
binding.loader.setVisibility(View.GONE);
binding.mediaPicture.setVisibility(View.GONE);
player.prepare(videoSource);
player.setPlayWhenReady(true);
break; break;
case "web": case "web":
binding.loader.setVisibility(View.GONE); binding.loader.setVisibility(View.GONE);
@ -261,6 +249,35 @@ public class FragmentMedia extends Fragment {
} }
} }
private void loadVideo(String url, String type) {
binding.pbarInf.setIndeterminate(false);
binding.pbarInf.setScaleY(3f);
binding.mediaVideo.setVisibility(View.VISIBLE);
Uri uri = Uri.parse(url);
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(requireActivity());
String userAgent = sharedpreferences.getString(getString(R.string.SET_CUSTOM_USER_AGENT), Helper.USER_AGENT);
int video_cache = sharedpreferences.getInt(getString(R.string.SET_VIDEO_CACHE), Helper.DEFAULT_VIDEO_CACHE_MB);
ProgressiveMediaSource videoSource;
if (video_cache == 0) {
DataSource.Factory dataSourceFactory = new DefaultDataSourceFactory(requireActivity(),
Util.getUserAgent(requireActivity(), userAgent), null);
videoSource = new ProgressiveMediaSource.Factory(dataSourceFactory)
.createMediaSource(uri);
} else {
CacheDataSourceFactory cacheDataSourceFactory = new CacheDataSourceFactory(requireActivity());
videoSource = new ProgressiveMediaSource.Factory(cacheDataSourceFactory)
.createMediaSource(uri);
}
player = new SimpleExoPlayer.Builder(requireActivity()).build();
if (type.equalsIgnoreCase("gifv"))
player.setRepeatMode(Player.REPEAT_MODE_ONE);
binding.mediaVideo.setPlayer(player);
binding.loader.setVisibility(View.GONE);
binding.mediaPicture.setVisibility(View.GONE);
player.prepare(videoSource);
player.setPlayWhenReady(true);
}
@Override @Override
public void onCreate(Bundle saveInstance) { public void onCreate(Bundle saveInstance) {
super.onCreate(saveInstance); super.onCreate(saveInstance);

View file

@ -71,6 +71,7 @@ public class TimelinesVM extends AndroidViewModel {
private MutableLiveData<List<StatusDraft>> statusDraftListMutableLiveData; private MutableLiveData<List<StatusDraft>> statusDraftListMutableLiveData;
private MutableLiveData<Status> statusMutableLiveData; private MutableLiveData<Status> statusMutableLiveData;
private MutableLiveData<Statuses> statusesMutableLiveData; private MutableLiveData<Statuses> statusesMutableLiveData;
private MutableLiveData<PeertubeVideo.Video> peertubeVideoMutableLiveData;
private MutableLiveData<Conversations> conversationListMutableLiveData; private MutableLiveData<Conversations> conversationListMutableLiveData;
private MutableLiveData<MastodonList> mastodonListMutableLiveData; private MutableLiveData<MastodonList> mastodonListMutableLiveData;
private MutableLiveData<List<MastodonList>> mastodonListListMutableLiveData; private MutableLiveData<List<MastodonList>> mastodonListListMutableLiveData;
@ -246,6 +247,34 @@ public class TimelinesVM extends AndroidViewModel {
} }
/**
* Returns details for a peertube video
*
* @return {@link LiveData} containing a {@link PeertubeVideo.Video}
*/
public LiveData<PeertubeVideo.Video> getPeertubeVideo(@NonNull String instance, String id) {
MastodonTimelinesService mastodonTimelinesService = initInstanceOnly(instance);
peertubeVideoMutableLiveData = new MutableLiveData<>();
new Thread(() -> {
Call<PeertubeVideo.Video> publicTlCall = mastodonTimelinesService.getPeertubeVideo(id);
PeertubeVideo.Video peertubeVideo = null;
try {
Response<PeertubeVideo.Video> videoResponse = publicTlCall.execute();
if (videoResponse.isSuccessful()) {
peertubeVideo = videoResponse.body();
}
} catch (Exception e) {
e.printStackTrace();
}
Handler mainHandler = new Handler(Looper.getMainLooper());
PeertubeVideo.Video finalPeertubeVideo = peertubeVideo;
Runnable myRunnable = () -> peertubeVideoMutableLiveData.setValue(finalPeertubeVideo);
mainHandler.post(myRunnable);
}).start();
return peertubeVideoMutableLiveData;
}
/** /**
* View public statuses containing the given hashtag. * View public statuses containing the given hashtag.
* *