package app.fedilab.fedilabtube.client; /* Copyright 2020 Thomas Schneider * * This file is a part of TubeLab * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 3 of the * License, or (at your option) any later version. * * TubeLab is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General * Public License for more details. * * You should have received a copy of the GNU General Public License along with TubeLab; if not, * see . */ import android.app.Activity; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.database.sqlite.SQLiteDatabase; import android.net.Uri; import android.os.Handler; import android.os.Looper; import android.webkit.MimeTypeMap; import androidx.documentfile.provider.DocumentFile; import org.jetbrains.annotations.NotNull; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.UnsupportedEncodingException; import java.net.URL; import java.net.URLDecoder; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Set; import app.fedilab.fedilabtube.BuildConfig; import app.fedilab.fedilabtube.MainActivity; import app.fedilab.fedilabtube.R; import app.fedilab.fedilabtube.client.data.AccountData; import app.fedilab.fedilabtube.client.data.BlockData; import app.fedilab.fedilabtube.client.data.CaptionData; import app.fedilab.fedilabtube.client.data.ChannelData; import app.fedilab.fedilabtube.client.data.CommentData; import app.fedilab.fedilabtube.client.data.InstanceData; import app.fedilab.fedilabtube.client.data.NotificationData; import app.fedilab.fedilabtube.client.data.PlaylistData; import app.fedilab.fedilabtube.client.data.VideoData; import app.fedilab.fedilabtube.client.data.VideoPlaylistData; import app.fedilab.fedilabtube.client.entities.AccountCreation; import app.fedilab.fedilabtube.client.entities.ChannelParams; import app.fedilab.fedilabtube.client.entities.Error; import app.fedilab.fedilabtube.client.entities.InstanceParams; import app.fedilab.fedilabtube.client.entities.Oauth; import app.fedilab.fedilabtube.client.entities.OauthParams; import app.fedilab.fedilabtube.client.entities.OverviewVideo; import app.fedilab.fedilabtube.client.entities.PeertubeInformation; import app.fedilab.fedilabtube.client.entities.PlaylistExist; import app.fedilab.fedilabtube.client.entities.PlaylistParams; import app.fedilab.fedilabtube.client.entities.Rating; import app.fedilab.fedilabtube.client.entities.Report; import app.fedilab.fedilabtube.client.entities.Token; import app.fedilab.fedilabtube.client.entities.UserMe; import app.fedilab.fedilabtube.client.entities.UserSettings; import app.fedilab.fedilabtube.client.entities.VideoParams; import app.fedilab.fedilabtube.client.entities.WellKnownNodeinfo; import app.fedilab.fedilabtube.helper.Helper; import app.fedilab.fedilabtube.sqlite.AccountDAO; import app.fedilab.fedilabtube.sqlite.Sqlite; import app.fedilab.fedilabtube.viewmodel.ChannelsVM; import app.fedilab.fedilabtube.viewmodel.CommentVM; import app.fedilab.fedilabtube.viewmodel.PlaylistsVM; import app.fedilab.fedilabtube.viewmodel.TimelineVM; import okhttp3.MediaType; import okhttp3.MultipartBody; import okhttp3.RequestBody; import okhttp3.ResponseBody; import retrofit2.Call; import retrofit2.Response; import retrofit2.Retrofit; import retrofit2.converter.gson.GsonConverterFactory; @SuppressWarnings({"unused", "RedundantSuppression"}) public class RetrofitPeertubeAPI { private final String finalUrl; private final Context _context; private final String instance; private final String count; private String token; private Set selection; public RetrofitPeertubeAPI(Context context) { _context = context; instance = Helper.getLiveInstance(context); finalUrl = "https://" + Helper.getLiveInstance(context) + "/api/v1/"; SharedPreferences sharedpreferences = _context.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE); count = String.valueOf(sharedpreferences.getInt(Helper.SET_VIDEOS_PER_PAGE, Helper.VIDEOS_PER_PAGE)); } public RetrofitPeertubeAPI(Context context, String instance, String token) { _context = context; this.instance = instance; this.token = token; finalUrl = "https://" + instance + "/api/v1/"; SharedPreferences sharedpreferences = _context.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE); count = String.valueOf(sharedpreferences.getInt(Helper.SET_VIDEOS_PER_PAGE, Helper.VIDEOS_PER_PAGE)); } public static void updateCredential(Activity activity, String token, String client_id, String client_secret, String refresh_token, String host) { new Thread(() -> { AccountData.Account account; String instance; if (host.startsWith("tube") || BuildConfig.full_instances) { instance = host; } else { instance = Helper.getPeertubeUrl(host); } try { UserMe userMe = new RetrofitPeertubeAPI(activity, instance, token).verifyCredentials(); account = userMe.getAccount(); } catch (Error error) { Error.displayError(activity, error); error.printStackTrace(); return; } try { //At the state the instance can be encoded instance = URLDecoder.decode(instance, "utf-8"); } catch (UnsupportedEncodingException ignored) { } SharedPreferences sharedpreferences = activity.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE); account.setToken(token); account.setClient_id(client_id); account.setClient_secret(client_secret); account.setRefresh_token(refresh_token); account.setHost(instance); SQLiteDatabase db = Sqlite.getInstance(activity.getApplicationContext(), Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open(); boolean userExists = new AccountDAO(activity, db).userExist(account); SharedPreferences.Editor editor = sharedpreferences.edit(); editor.putString(Helper.PREF_KEY_ID, account.getId()); editor.putString(Helper.PREF_KEY_NAME, account.getUsername()); if (!host.startsWith("tube")) { editor.putString(Helper.PREF_INSTANCE, host); } editor.apply(); if (userExists) new AccountDAO(activity, db).updateAccountCredential(account); else { if (account.getUsername() != null && account.getCreatedAt() != null) new AccountDAO(activity, db).insertAccount(account); } Handler mainHandler = new Handler(Looper.getMainLooper()); Runnable myRunnable = () -> { Intent mainActivity = new Intent(activity, MainActivity.class); mainActivity.putExtra(Helper.INTENT_ACTION, Helper.ADD_USER_INTENT); activity.startActivity(mainActivity); activity.finish(); }; mainHandler.post(myRunnable); }).start(); } private String getToken() { if (token != null) { return "Bearer " + token; } else { return null; } } private PeertubeService init() { Retrofit retrofit = new Retrofit.Builder() .baseUrl(finalUrl) .addConverterFactory(GsonConverterFactory.create()) .build(); SharedPreferences sharedpreferences = _context.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE); if (token == null) { token = sharedpreferences.getString(Helper.PREF_KEY_OAUTH_TOKEN, null); } selection = sharedpreferences.getStringSet(_context.getString(R.string.set_video_language_choice), null); return retrofit.create(PeertubeService.class); } private PeertubeService initTranslation() { Retrofit retrofit = new Retrofit.Builder() .baseUrl("https://" + instance) .addConverterFactory(GsonConverterFactory.create()) .build(); return retrofit.create(PeertubeService.class); } /*** * Verifiy credential of the authenticated user *synchronously* * @return Account */ public Token manageToken(OauthParams oauthParams) throws Error { PeertubeService peertubeService = init(); Call refreshTokenCall = null; if (oauthParams.getGrant_type().compareTo("password") == 0) { refreshTokenCall = peertubeService.createToken(oauthParams.getClient_id(), oauthParams.getClient_secret(), oauthParams.getGrant_type(), oauthParams.getUsername(), oauthParams.getPassword()); } else if (oauthParams.getGrant_type().compareTo("refresh_token") == 0) { refreshTokenCall = peertubeService.refreshToken(oauthParams.getClient_id(), oauthParams.getClient_secret(), oauthParams.getRefresh_token(), oauthParams.getGrant_type()); } if (refreshTokenCall != null) { try { Response response = refreshTokenCall.execute(); if (response.isSuccessful()) { Token tokenReply = response.body(); if (oauthParams.getGrant_type().compareTo("refresh_token") == 0 && tokenReply != null) { SharedPreferences sharedpreferences = _context.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE); SharedPreferences.Editor editor = sharedpreferences.edit(); editor.putString(Helper.PREF_KEY_OAUTH_TOKEN, tokenReply.getAccess_token()); editor.apply(); SQLiteDatabase db = Sqlite.getInstance(_context.getApplicationContext(), Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open(); new AccountDAO(_context, db).updateAccountToken(tokenReply); } return tokenReply; } else { Error error = new Error(); error.setStatusCode(response.code()); if (response.errorBody() != null) { error.setError(response.errorBody().string()); } else { error.setError(_context.getString(R.string.toast_error)); } throw error; } } catch (IOException e) { e.printStackTrace(); } } return null; } /** * Retrieve notifications * * @return APIResponse */ public APIResponse getNotifications() { APIResponse apiResponse = new APIResponse(); PeertubeService peertubeService = init(); Call notificationsCall = peertubeService.getNotifications("Bearer " + token, "0", "40", null); try { Response response = notificationsCall.execute(); if (response.isSuccessful() && response.body() != null) { apiResponse.setPeertubeNotifications(response.body().data); } else { setError(apiResponse, response.code(), response.errorBody()); } } catch (IOException e) { Error error = new Error(); error.setError(_context.getString(R.string.toast_error)); apiResponse.setError(error); e.printStackTrace(); } return apiResponse; } /** * Retrieve notifications * * @param max_id String pagination * @param since_id String pagination * @return APIResponse */ public APIResponse getNotifications(String max_id, String since_id) { APIResponse apiResponse = new APIResponse(); PeertubeService peertubeService = init(); Call notificationsCall = peertubeService.getNotifications("Bearer " + token, max_id, "20", since_id); try { Response response = notificationsCall.execute(); if (response.isSuccessful() && response.body() != null) { apiResponse.setPeertubeNotifications(response.body().data); } else { setError(apiResponse, response.code(), response.errorBody()); } } catch (IOException e) { Error error = new Error(); error.setError(_context.getString(R.string.toast_error)); apiResponse.setError(error); e.printStackTrace(); } return apiResponse; } /** * Get caption content * * @param path String path to caption * @return APIResponse */ public APIResponse getCaptionContent(String path) { APIResponse apiResponse = new APIResponse(); PeertubeService peertubeService = init(); Call captionContentCall = peertubeService.getCaptionContent(path); try { Response response = captionContentCall.execute(); if (response.isSuccessful()) { apiResponse.setCaptionText(response.body()); } else { setError(apiResponse, response.code(), response.errorBody()); } } catch (IOException e) { Error error = new Error(); error.setError(_context.getString(R.string.toast_error)); apiResponse.setError(error); e.printStackTrace(); } return apiResponse; } /** * Get videos in a channel * * @param channelId String id of the channel * @param max_id String pagination * @return APIResponse */ public APIResponse getVideosForChannel(String channelId, String max_id) { APIResponse apiResponse = new APIResponse(); PeertubeService peertubeService = init(); Call videoCall = peertubeService.getChannelVideos(channelId, max_id, count); if (videoCall != null) { try { Response response = videoCall.execute(); if (response.isSuccessful() && response.body() != null) { apiResponse.setPeertubes(response.body().data); } else { setError(apiResponse, response.code(), response.errorBody()); } } catch (IOException e) { Error error = new Error(); error.setError(_context.getString(R.string.toast_error)); apiResponse.setError(error); e.printStackTrace(); } } return apiResponse; } public APIResponse deleteHistory() { APIResponse apiResponse = new APIResponse(); PeertubeService peertubeService = init(); Call stringCall = peertubeService.deleteHistory(getToken()); if (stringCall != null) { try { Response response = stringCall.execute(); if (response.isSuccessful() && response.body() != null) { apiResponse.setActionReturn(response.body()); } else { setError(apiResponse, response.code(), response.errorBody()); } } catch (IOException e) { Error error = new Error(); error.setError(_context.getString(R.string.toast_error)); apiResponse.setError(error); e.printStackTrace(); } } return apiResponse; } public APIResponse getHistory(String max_id, String startDate, String endDate) { APIResponse apiResponse = new APIResponse(); PeertubeService peertubeService = init(); Call videoCall = peertubeService.getHistory(getToken(), max_id, count, startDate, endDate); if (videoCall != null) { try { Response response = videoCall.execute(); if (response.isSuccessful() && response.body() != null) { apiResponse.setPeertubes(response.body().data); } else { setError(apiResponse, response.code(), response.errorBody()); } } catch (IOException e) { Error error = new Error(); error.setError(_context.getString(R.string.toast_error)); apiResponse.setError(error); e.printStackTrace(); } } return apiResponse; } public APIResponse getTL(TimelineVM.TimelineType timelineType, String max_id, String forAccount) { APIResponse apiResponse = new APIResponse(); PeertubeService peertubeService = init(); Call videoCall = null; ArrayList filter = selection != null ? new ArrayList<>(selection) : null; switch (timelineType) { case MY_VIDEOS: videoCall = peertubeService.getMyVideos(getToken(), max_id, count); break; case ACCOUNT_VIDEOS: videoCall = peertubeService.getVideosForAccount(forAccount, max_id, count); break; case SUBSCRIBTIONS: if (forAccount == null) { videoCall = peertubeService.getSubscriptionVideos(getToken(), max_id, count, filter); } else { videoCall = peertubeService.getChannelVideos(forAccount, max_id, count); } break; case MOST_LIKED: videoCall = peertubeService.getMostLikedVideos(getToken(), max_id, count, filter); break; case LOCAL: videoCall = peertubeService.getLocalVideos(getToken(), max_id, count, filter); break; case TRENDING: videoCall = peertubeService.getTrendingVideos(getToken(), max_id, count, filter); break; case HISTORY: videoCall = peertubeService.getHistory(getToken(), max_id, count, null, null); break; case RECENT: videoCall = peertubeService.getRecentlyAddedVideos(getToken(), max_id, count, filter); break; } if (videoCall != null) { try { Response response = videoCall.execute(); if (response.isSuccessful() && response.body() != null) { apiResponse.setPeertubes(response.body().data); } else { setError(apiResponse, response.code(), response.errorBody()); } } catch (IOException e) { Error error = new Error(); error.setError(_context.getString(R.string.toast_error)); apiResponse.setError(error); e.printStackTrace(); } } return apiResponse; } /** * Retrieves overview videos *synchronously* * * @param page String id pagination * @return APIResponse */ public APIResponse getOverviewVideo(String page) { APIResponse apiResponse = new APIResponse(); PeertubeService peertubeService = init(); ArrayList filter = selection != null ? new ArrayList<>(selection) : null; Call overviewVideoCall = peertubeService.getOverviewVideos(getToken(), page, filter); try { Response response = overviewVideoCall.execute(); if (response.isSuccessful() && response.body() != null) { apiResponse.setOverviewVideo(response.body()); } else { setError(apiResponse, response.code(), response.errorBody()); } } catch (IOException e) { Error error = new Error(); error.setError(_context.getString(R.string.toast_error)); apiResponse.setError(error); e.printStackTrace(); } return apiResponse; } /** * Retrieves playlists for a video *synchronously* * * @param videoIds List ids of videos * @return APIResponse */ public APIResponse getVideosExist(List videoIds) { PeertubeService peertubeService = init(); APIResponse apiResponse = new APIResponse(); try { Call>> videoExistsInPlaylist = peertubeService.getVideoExistsInPlaylist(getToken(), videoIds); Response>> response = videoExistsInPlaylist.execute(); if (response.isSuccessful() && response.body() != null) { apiResponse.setVideoExistPlaylist(response.body()); } else { setError(apiResponse, response.code(), response.errorBody()); } } catch (IOException e) { e.printStackTrace(); } return apiResponse; } /** * Update history * * @param videoId String * @param currentTime int */ public void updateHistory(String videoId, long currentTime) { APIResponse apiResponse = new APIResponse(); PeertubeService peertubeService = init(); Call updateUser = peertubeService.addToHistory(getToken(), videoId, currentTime ); try { Response response = updateUser.execute(); if (response.isSuccessful()) { apiResponse.setActionReturn(response.body()); } else { setError(apiResponse, response.code(), response.errorBody()); } } catch (IOException e) { e.printStackTrace(); } } /** * Update account information * * @param userSettings UserSettings */ public UserMe.AvatarResponse updateUser(UserSettings userSettings) throws IOException, Error { APIResponse apiResponse = new APIResponse(); UserMe.AvatarResponse avatarResponse = null; PeertubeService peertubeService = init(); Call updateNotifications = peertubeService.updateNotifications(getToken(), userSettings.getNotificationSettings()); Response responseNotif = updateNotifications.execute(); Call updateUser = peertubeService.updateUser(getToken(), userSettings.isVideosHistoryEnabled(), userSettings.isAutoPlayVideo(), userSettings.isAutoPlayNextVideo(), userSettings.isWebTorrentEnabled(), userSettings.getVideoLanguages(), userSettings.getDescription(), userSettings.getDisplayName(), userSettings.getNsfwPolicy() ); Response response = updateUser.execute(); if (response.isSuccessful()) { apiResponse.setActionReturn(response.body()); } else { setError(apiResponse, response.code(), response.errorBody()); Error error = new Error(); error.setStatusCode(response.code()); if (response.errorBody() != null) { error.setError(response.errorBody().string()); } else { error.setError(_context.getString(R.string.toast_error)); } throw error; } if (userSettings.getAvatarfile() != null) { MultipartBody.Part bodyThumbnail = createFile("avatarfile", userSettings.getAvatarfile(), userSettings.getFileName()); Call updateProfilePicture = peertubeService.updateProfilePicture(getToken(), bodyThumbnail); Response responseAvatar = updateProfilePicture.execute(); if (response.isSuccessful()) { avatarResponse = responseAvatar.body(); } else { setError(apiResponse, response.code(), response.errorBody()); Error error = new Error(); error.setStatusCode(response.code()); if (response.errorBody() != null) { error.setError(response.errorBody().string()); } else { error.setError(_context.getString(R.string.toast_error)); } throw error; } } return avatarResponse; } private MultipartBody.Part createFile(@NotNull String paramName, @NotNull Uri uri, String filename) throws IOException { InputStream inputStream = _context.getContentResolver().openInputStream(uri); ByteArrayOutputStream byteBuffer = new ByteArrayOutputStream(); int bufferSize = 1024; byte[] buffer = new byte[bufferSize]; int len; while ((len = inputStream.read(buffer)) != -1) { byteBuffer.write(buffer, 0, len); } byte[] imageBytes = byteBuffer.toByteArray(); String mime = MimeTypeMap.getFileExtensionFromUrl(uri.toString()); if (mime == null || mime.trim().length() == 0) { mime = "png"; } if (filename == null) { filename = "my_image." + mime; } RequestBody requestFile = RequestBody.create(MediaType.parse("image/" + mime), imageBytes); return MultipartBody.Part.createFormData(paramName, filename, requestFile); } /** * Check if users via their uris are following the authenticated user * * @param uris List * @return APIResponse */ public APIResponse areFollowing(List uris) { APIResponse apiResponse = new APIResponse(); PeertubeService peertubeService = init(); Call> followingCall = peertubeService.getSubscriptionsExist(getToken(), uris); try { Response> response = followingCall.execute(); if (response.isSuccessful()) { apiResponse.setRelationships(response.body()); } else { setError(apiResponse, response.code(), response.errorBody()); } } catch (IOException e) { e.printStackTrace(); } return apiResponse; } /** * Find captions for a video * * @param videoId String id of the video * @return APIResponse */ public APIResponse getCaptions(String videoId) { APIResponse apiResponse = new APIResponse(); PeertubeService peertubeService = init(); Call captions = peertubeService.getCaptions(videoId); try { Response response = captions.execute(); if (response.isSuccessful() && response.body() != null) { apiResponse.setCaptions(response.body().data); } else { setError(apiResponse, response.code(), response.errorBody()); } } catch (IOException e) { e.printStackTrace(); } return apiResponse; } /** * About the instance * * @return AboutInstance */ public InstanceData.AboutInstance getAboutInstance() { PeertubeService peertubeService = init(); Call about = peertubeService.configAbout(); try { Response response = about.execute(); if (response.isSuccessful() && response.body() != null) { return response.body().getInstance(); } } catch (IOException e) { e.printStackTrace(); } return null; } /** * Returns informations about Peertube such privacies, licenses, etc. * * @return PeertubeInformation information about peertube */ public PeertubeInformation getPeertubeInformation() { PeertubeInformation peertubeInformation = new PeertubeInformation(); PeertubeService peertubeService = init(); Call> categories = peertubeService.getCategories(); try { Response> response = categories.execute(); if (response.isSuccessful()) { peertubeInformation.setCategories(response.body()); } else { Error error = new Error(); error.setStatusCode(response.code()); if (response.errorBody() != null) { error.setError(response.errorBody().string()); } else { error.setError(_context.getString(R.string.toast_error)); } } } catch (IOException e) { e.printStackTrace(); } Call> languages = peertubeService.getLanguages(); try { Response> response = languages.execute(); if (response.isSuccessful()) { peertubeInformation.setLanguages(response.body()); } else { Error error = new Error(); error.setStatusCode(response.code()); if (response.errorBody() != null) { error.setError(response.errorBody().string()); } else { error.setError(_context.getString(R.string.toast_error)); } } } catch (IOException e) { e.printStackTrace(); } Call> privacies = peertubeService.getPrivacies(); try { Response> response = privacies.execute(); if (response.isSuccessful()) { peertubeInformation.setPrivacies(response.body()); } else { Error error = new Error(); error.setStatusCode(response.code()); if (response.errorBody() != null) { error.setError(response.errorBody().string()); } else { error.setError(_context.getString(R.string.toast_error)); } } } catch (IOException e) { e.printStackTrace(); } Call> playlistsPrivacies = peertubeService.getPlaylistsPrivacies(); try { Response> response = playlistsPrivacies.execute(); if (response.isSuccessful()) { peertubeInformation.setPlaylistPrivacies(response.body()); } else { Error error = new Error(); error.setStatusCode(response.code()); if (response.errorBody() != null) { error.setError(response.errorBody().string()); } else { error.setError(_context.getString(R.string.toast_error)); } } } catch (IOException e) { e.printStackTrace(); } Call> licenses = peertubeService.getLicences(); try { Response> response = licenses.execute(); if (response.isSuccessful()) { peertubeInformation.setLicences(response.body()); } else { Error error = new Error(); error.setStatusCode(response.code()); if (response.errorBody() != null) { error.setError(response.errorBody().string()); } else { error.setError(_context.getString(R.string.toast_error)); } } } catch (IOException e) { e.printStackTrace(); } String lang = Locale.getDefault().getLanguage(); if (lang.contains("-")) { if (!lang.split("-")[0].trim().toLowerCase().startsWith("zh")) { lang = lang.split("-")[0]; } else { lang = lang.split("-")[0] + "-" + lang.split("-")[1].toUpperCase(); } } if (lang == null || lang.trim().length() == 0) { lang = "en"; } Call> translations = initTranslation().getTranslations(lang); try { Response> response = translations.execute(); if (response.isSuccessful()) { peertubeInformation.setTranslations(response.body()); } else { Error error = new Error(); error.setStatusCode(response.code()); if (response.errorBody() != null) { translations = initTranslation().getTranslations("en"); try { response = translations.execute(); if (response.isSuccessful()) { peertubeInformation.setTranslations(response.body()); } else { error = new Error(); error.setStatusCode(response.code()); if (response.errorBody() != null) { error.setError(response.errorBody().string()); } else { error.setError(_context.getString(R.string.toast_error)); } } } catch (IOException e) { e.printStackTrace(); } } else { error.setError(_context.getString(R.string.toast_error)); } } } catch (IOException e) { e.printStackTrace(); } return peertubeInformation; } /** * Get instances * * @param instanceParams InstanceParams * @return APIResponse */ public APIResponse getInstances(InstanceParams instanceParams) { PeertubeService peertubeService = init(); LinkedHashMap params = new LinkedHashMap<>(); params.put("start", "0"); params.put("count", "250"); params.put("healthy", "true"); params.put("signup", "true"); params.put("minUserQuota", instanceParams.getMinUserQuota()); Call instancesCall = peertubeService.getInstances(params, instanceParams.getNsfwPolicy(), instanceParams.getCategoriesOr(), instanceParams.getLanguagesOr()); APIResponse apiResponse = new APIResponse(); try { Response response = instancesCall.execute(); if (!response.isSuccessful()) { setError(apiResponse, response.code(), response.errorBody()); } else { InstanceData instanceData = response.body(); if (instanceData != null) { apiResponse.setInstances(instanceData.data); } } } catch (IOException e) { Error error = new Error(); error.setError(_context.getString(R.string.toast_error)); apiResponse.setError(error); e.printStackTrace(); } return apiResponse; } /** * Retrieves next peertube videos *synchronously* * * @param tags List search * @return APIResponse */ public APIResponse searchNextVideos(List tags) { PeertubeService peertubeService = init(); Call searchVideosCall = peertubeService.searchNextVideo(getToken(), tags, "0", "20"); APIResponse apiResponse = new APIResponse(); try { Response response = searchVideosCall.execute(); if (response.isSuccessful() && response.body() != null) { apiResponse.setPeertubes(response.body().data); } else { setError(apiResponse, response.code(), response.errorBody()); } } catch (IOException e) { Error error = new Error(); error.setError(_context.getString(R.string.toast_error)); apiResponse.setError(error); e.printStackTrace(); } return apiResponse; } /** * Retrieves peertube search *synchronously* * * @param query String search * @return APIResponse */ public APIResponse searchPeertube(String query, String max_id) { PeertubeService peertubeService = init(); Call searchVideosCall = peertubeService.searchVideos(getToken(), query, max_id, count); APIResponse apiResponse = new APIResponse(); try { Response response = searchVideosCall.execute(); if (response.isSuccessful() && response.body() != null) { apiResponse.setPeertubes(response.body().data); } else { setError(apiResponse, response.code(), response.errorBody()); } } catch (IOException e) { Error error = new Error(); error.setError(_context.getString(R.string.toast_error)); apiResponse.setError(error); e.printStackTrace(); } return apiResponse; } /*** * Verifiy credential of the authenticated user *synchronously* * @return Account */ public UserMe verifyCredentials() throws Error { PeertubeService peertubeService = init(); Call accountCall = peertubeService.verifyCredentials("Bearer " + token); APIResponse apiResponse = new APIResponse(); try { Response response = accountCall.execute(); if (response.isSuccessful() && response.body() != null) { return response.body(); } else { Error error = new Error(); error.setStatusCode(response.code()); if (response.errorBody() != null) { error.setError(response.errorBody().string()); } else { error.setError(_context.getString(R.string.toast_error)); } throw error; } } catch (IOException e) { Error error = new Error(); error.setError(_context.getString(R.string.toast_error)); apiResponse.setError(error); e.printStackTrace(); } return null; } public APIResponse report(Report report) { PeertubeService peertubeService = init(); Call report1 = peertubeService.report(getToken(), report); APIResponse apiResponse = new APIResponse(); try { Response response = report1.execute(); if (response.isSuccessful() && response.body() != null) { apiResponse.setActionReturn(response.body().getItemStr().getLabel()); } else { setError(apiResponse, response.code(), response.errorBody()); } } catch (IOException e) { Error error = new Error(); error.setError(_context.getString(R.string.toast_error)); apiResponse.setError(error); e.printStackTrace(); } return apiResponse; } /*** * Update a video * @param videoId String id of the video * @param videoParams VideoParams params for the video * @param thumbnail File thumbnail * @param previewfile File preview * @return APIResponse */ public APIResponse updateVideo(String videoId, VideoParams videoParams, Uri thumbnail, Uri previewfile) { PeertubeService peertubeService = init(); MultipartBody.Part bodyThumbnail = null; MultipartBody.Part bodyPreviewfile = null; try { if (thumbnail != null) { DocumentFile documentFile = DocumentFile.fromSingleUri(_context, thumbnail); String thumbnailName = null; if (documentFile != null) { thumbnailName = documentFile.getName(); } bodyThumbnail = createFile("avatarfile", thumbnail, thumbnailName); } if (previewfile != null) { DocumentFile documentFile = DocumentFile.fromSingleUri(_context, thumbnail); String previewfileName = null; if (documentFile != null) { previewfileName = documentFile.getName(); } bodyPreviewfile = createFile("image", previewfile, previewfileName); } } catch (IOException e) { e.printStackTrace(); } RequestBody channelId = RequestBody.create(MediaType.parse("text/plain"), videoParams.getChannelId()); RequestBody description = RequestBody.create(MediaType.parse("text/plain"), videoParams.getDescription()); RequestBody language = RequestBody.create(MediaType.parse("text/plain"), videoParams.getLanguage()); RequestBody license = RequestBody.create(MediaType.parse("text/plain"), videoParams.getLicence()); RequestBody name = RequestBody.create(MediaType.parse("text/plain"), videoParams.getName()); List tags = null; if (videoParams.getTags() != null && videoParams.getTags().size() > 0) { tags = new ArrayList<>(); for (String tag : videoParams.getTags()) { tags.add(RequestBody.create(MediaType.parse("text/plain"), tag)); } } RequestBody support = null; if (videoParams.getSupport() != null) { support = RequestBody.create(MediaType.parse("text/plain"), videoParams.getSupport()); } Call upload = peertubeService.updateVideo(getToken(), videoId, channelId, name, videoParams.getCategory(), videoParams.isCommentsEnabled(), description, videoParams.isDownloadEnabled(), language, license, videoParams.isNsfw(), videoParams.getPrivacy(), support, tags, videoParams.isWaitTranscoding() , bodyThumbnail, bodyPreviewfile); APIResponse apiResponse = new APIResponse(); try { Response response = upload.execute(); if (response.isSuccessful()) { apiResponse.setActionReturn("ok"); } else { setError(apiResponse, response.code(), response.errorBody()); } } catch (IOException e) { Error error = new Error(); error.setError(_context.getString(R.string.toast_error)); apiResponse.setError(error); e.printStackTrace(); } return apiResponse; } public APIResponse createAccount(AccountCreation accountCreation) { PeertubeService peertubeService = init(); Call report1 = peertubeService.register(accountCreation.getEmail(), accountCreation.getPassword(), accountCreation.getUsername(), accountCreation.getDisplayName()); APIResponse apiResponse = new APIResponse(); try { Response response = report1.execute(); if (response.isSuccessful()) { apiResponse.setActionReturn(accountCreation.getEmail()); } else { setError(apiResponse, response.code(), response.errorBody()); } } catch (IOException e) { Error error = new Error(); error.setError(_context.getString(R.string.toast_error)); apiResponse.setError(error); e.printStackTrace(); } return apiResponse; } public APIResponse post(ActionType actionType, String id, String element) { PeertubeService peertubeService = init(); Call postCall = null; APIResponse apiResponse = new APIResponse(); switch (actionType) { case FOLLOW: postCall = peertubeService.follow(getToken(), id); break; case UNFOLLOW: postCall = peertubeService.unfollow(getToken(), id); break; case MUTE: postCall = peertubeService.mute(getToken(), id); break; case UNMUTE: postCall = peertubeService.unmute(getToken(), id); break; case RATEVIDEO: postCall = peertubeService.rate(getToken(), id, element); break; case PEERTUBEDELETEVIDEO: postCall = peertubeService.deleteVideo(getToken(), id); break; case PEERTUBEDELETECOMMENT: postCall = peertubeService.deleteComment(getToken(), id, element); break; case PEERTUBE_DELETE_ALL_COMMENT_FOR_ACCOUNT: postCall = peertubeService.deleteAllCommentForAccount(getToken(), id, element); break; case DELETE_CHANNEL: postCall = peertubeService.deleteChannel(getToken(), id); break; } if (postCall != null) { try { Response response = postCall.execute(); if (response.isSuccessful()) { apiResponse.setActionReturn(response.body()); } else { setError(apiResponse, response.code(), response.errorBody()); } } catch (IOException e) { Error error = new Error(); error.setError(_context.getString(R.string.toast_error)); apiResponse.setError(error); e.printStackTrace(); } } return apiResponse; } /** * Get single account by its handle * * @param accountHandle String * @return APIResponse */ public APIResponse getAccount(String accountHandle) { PeertubeService peertubeService = init(); Call accountDataCall = peertubeService.getAccount(accountHandle); APIResponse apiResponse = new APIResponse(); if (accountDataCall != null) { try { Response response = accountDataCall.execute(); if (response.isSuccessful() && response.body() != null) { List accountList = new ArrayList<>(); accountList.add(response.body()); apiResponse.setAccounts(accountList); } else { setError(apiResponse, response.code(), response.errorBody()); } } catch (IOException e) { Error error = new Error(); error.setError(_context.getString(R.string.toast_error)); apiResponse.setError(error); e.printStackTrace(); } } return apiResponse; } /** * Get video description * * @param uuid String (pagination) * @return APIResponse */ public VideoData.Description getVideoDescription(String uuid) { PeertubeService peertubeService = init(); Call videoDescription = peertubeService.getVideoDescription(uuid); try { Response response = videoDescription.execute(); if (response.isSuccessful() && response.body() != null) { return response.body(); } } catch (IOException ignored) { } return null; } /** * Get muted accounts * * @param maxId String (pagination) * @return APIResponse */ public APIResponse getMuted(String maxId) { PeertubeService peertubeService = init(); Call accountDataCall = peertubeService.getMuted("Bearer " + token, maxId, count); APIResponse apiResponse = new APIResponse(); if (accountDataCall != null) { try { Response response = accountDataCall.execute(); if (response.isSuccessful() && response.body() != null) { apiResponse.setMuted(response.body().getData()); } else { setError(apiResponse, response.code(), response.errorBody()); } } catch (IOException e) { Error error = new Error(); error.setError(_context.getString(R.string.toast_error)); apiResponse.setError(error); e.printStackTrace(); } } return apiResponse; } /** * Get subscriptions data * * @param maxId String (pagination) * @return APIResponse */ public APIResponse getSubscribtions(String maxId) { PeertubeService peertubeService = init(); Call channelDataCall = peertubeService.getSubscription("Bearer " + token, maxId, count); APIResponse apiResponse = new APIResponse(); if (channelDataCall != null) { try { Response response = channelDataCall.execute(); if (response.isSuccessful() && response.body() != null) { apiResponse.setChannels(response.body().data); } else { setError(apiResponse, response.code(), response.errorBody()); } } catch (IOException e) { Error error = new Error(); error.setError(_context.getString(R.string.toast_error)); apiResponse.setError(error); e.printStackTrace(); } } return apiResponse; } /** * Create or update a channel * * @param apiAction ChannelsVM.action * @param channelId String * @param channelParams PlaylistParams * @return APIResponse */ public APIResponse createOrUpdateChannel(ChannelsVM.action apiAction, String channelId, ChannelParams channelParams) { PeertubeService peertubeService = init(); APIResponse apiResponse = new APIResponse(); try { if (apiAction == ChannelsVM.action.CREATE_CHANNEL) { Call stringCall = peertubeService.addChannel(getToken(), channelParams); Response response = stringCall.execute(); if (response.isSuccessful() && response.body() != null) { apiResponse.setActionReturn(response.body().getVideoChannel().getId()); } else { setError(apiResponse, response.code(), response.errorBody()); } } else if (apiAction == ChannelsVM.action.UPDATE_CHANNEL) { Call stringCall = peertubeService.updateChannel(getToken(), channelId, channelParams); Response response = stringCall.execute(); if (response.isSuccessful()) { apiResponse.setActionReturn(response.body()); } else { setError(apiResponse, response.code(), response.errorBody()); } } } catch (IOException e) { Error error = new Error(); error.setError(_context.getString(R.string.toast_error)); apiResponse.setError(error); e.printStackTrace(); } return apiResponse; } /** * Get Oauth * * @return APIResponse */ public Oauth oauthClient(String client_name, String redirect_uris, String scopes, String website) { PeertubeService peertubeService = init(); try { Call oauth; if (BuildConfig.full_instances) { oauth = peertubeService.getOauth(client_name, redirect_uris, scopes, website); } else { oauth = peertubeService.getOauthAcad(); } Response response = oauth.execute(); if (response.isSuccessful() && response.body() != null) { return response.body(); } } catch (IOException e) { e.printStackTrace(); } return null; } /** * Get NodeInfo * * @return APIResponse */ public WellKnownNodeinfo.NodeInfo getNodeInfo() { PeertubeService peertubeService = initTranslation(); try { Call wellKnownNodeinfoCall = peertubeService.getWellKnownNodeinfo(); Response response = wellKnownNodeinfoCall.execute(); if (response.isSuccessful() && response.body() != null) { int size = response.body().getLinks().size(); String url = response.body().getLinks().get(size - 1).getHref(); if (size > 0 && url != null) { peertubeService = initTranslation(); String path = new URL(url).getPath(); path = path.replaceFirst("/", "").trim(); Call nodeinfo = peertubeService.getNodeinfo(path); Response responseNodeInfo = nodeinfo.execute(); return responseNodeInfo.body(); } } } catch (IOException e) { e.printStackTrace(); } return null; } /** * Get channel data * * @param accountDataType AccountDataType (type of requested data) * @param element String (pagination or name for the channel) * @return APIResponse */ public APIResponse getChannelData(DataType accountDataType, String element) { PeertubeService peertubeService = init(); APIResponse apiResponse = new APIResponse(); switch (accountDataType) { case MY_CHANNELS: case CHANNELS_FOR_ACCOUNT: Call channelDataCall = peertubeService.getChannelsForAccount(element); try { Response response = channelDataCall.execute(); if (response.isSuccessful() && response.body() != null) { apiResponse.setChannels(response.body().data); } else { setError(apiResponse, response.code(), response.errorBody()); } } catch (IOException e) { Error error = new Error(); error.setError(_context.getString(R.string.toast_error)); apiResponse.setError(error); e.printStackTrace(); } break; case CHANNEL: Call channelCall = peertubeService.getChannel(element); try { Response response = channelCall.execute(); if (!response.isSuccessful()) { setError(apiResponse, response.code(), response.errorBody()); } else { ChannelData.Channel channelData = response.body(); if (channelData != null) { List channelList = new ArrayList<>(); channelList.add(channelData); apiResponse.setChannels(channelList); } } } catch (IOException e) { Error error = new Error(); error.setError(_context.getString(R.string.toast_error)); apiResponse.setError(error); e.printStackTrace(); } break; } return apiResponse; } /** * Create or update a playlist * * @param apiAction PlaylistsVM.action * @param playlistId String * @param playlistParams PlaylistParams * @return APIResponse */ public APIResponse createOrUpdatePlaylist(PlaylistsVM.action apiAction, String playlistId, PlaylistParams playlistParams, File thumbnail) { PeertubeService peertubeService = init(); APIResponse apiResponse = new APIResponse(); MultipartBody.Part body = null; if (thumbnail != null) { RequestBody requestFile = RequestBody.create(MediaType.parse("multipart/form-data"), thumbnail); body = MultipartBody.Part.createFormData("image", thumbnail.getName(), requestFile); } try { RequestBody displayName = RequestBody.create(MediaType.parse("text/plain"), playlistParams.getDisplayName()); RequestBody description = RequestBody.create(MediaType.parse("text/plain"), playlistParams.getDescription()); RequestBody channelId = RequestBody.create(MediaType.parse("text/plain"), playlistParams.getVideoChannelId()); if (apiAction == PlaylistsVM.action.CREATE_PLAYLIST) { Call stringCall = peertubeService.addPlaylist(getToken(), displayName, description, playlistParams.getPrivacy(), channelId, body); Response response = stringCall.execute(); if (response.isSuccessful() && response.body() != null) { apiResponse.setActionReturn(response.body().getVideoPlaylist().getId()); } else { setError(apiResponse, response.code(), response.errorBody()); } } else if (apiAction == PlaylistsVM.action.UPDATE_PLAYLIST) { Call stringCall = peertubeService.updatePlaylist(getToken(), playlistId, displayName, description, playlistParams.getPrivacy(), channelId, body); Response response = stringCall.execute(); if (response.isSuccessful()) { apiResponse.setActionReturn(response.body()); } else { setError(apiResponse, response.code(), response.errorBody()); } } } catch (IOException e) { Error error = new Error(); error.setError(_context.getString(R.string.toast_error)); apiResponse.setError(error); e.printStackTrace(); } return apiResponse; } /** * Retrieves playlist *synchronously* * * @param type PlaylistsVM.action * @param playlistId String id of the playlist * @param videoId String id of the video * @return APIResponse */ public APIResponse playlistAction(PlaylistsVM.action type, String playlistId, String videoId, String acct, String max_id) { PeertubeService peertubeService = init(); APIResponse apiResponse = new APIResponse(); try { if (type == PlaylistsVM.action.GET_PLAYLIST_INFO) { Call playlistCall = peertubeService.getPlaylist(playlistId); Response response = playlistCall.execute(); if (response.isSuccessful()) { List playlists = new ArrayList<>(); playlists.add(response.body()); apiResponse.setPlaylists(playlists); } else { setError(apiResponse, response.code(), response.errorBody()); } } else if (type == PlaylistsVM.action.GET_PLAYLISTS) { Call playlistsCall = peertubeService.getPlaylistsForAccount(getToken(), acct); Response response = playlistsCall.execute(); if (response.isSuccessful() && response.body() != null) { apiResponse.setPlaylists(response.body().data); } else { setError(apiResponse, response.code(), response.errorBody()); } } else if (type == PlaylistsVM.action.GET_LIST_VIDEOS) { Call videosPlayList = peertubeService.getVideosPlayList(getToken(), playlistId, max_id, count); Response response = videosPlayList.execute(); if (response.isSuccessful() && response.body() != null) { apiResponse.setVideoPlaylist(response.body().data); } else { setError(apiResponse, response.code(), response.errorBody()); } } else if (type == PlaylistsVM.action.DELETE_PLAYLIST) { Call stringCall = peertubeService.deletePlaylist(getToken(), playlistId); Response response = stringCall.execute(); if (response.isSuccessful()) { apiResponse.setActionReturn(response.body()); } else { setError(apiResponse, response.code(), response.errorBody()); } } else if (type == PlaylistsVM.action.ADD_VIDEOS) { Call stringCall = peertubeService.addVideoInPlaylist(getToken(), playlistId, videoId); Response response = stringCall.execute(); if (response.isSuccessful() && response.body() != null) { apiResponse.setActionReturn(response.body().getVideoPlaylistElement().getId()); } else { setError(apiResponse, response.code(), response.errorBody()); } } else if (type == PlaylistsVM.action.DELETE_VIDEOS) { Call stringCall = peertubeService.deleteVideoInPlaylist(getToken(), playlistId, videoId); Response response = stringCall.execute(); if (response.isSuccessful()) { apiResponse.setActionReturn(response.body()); } else { setError(apiResponse, response.code(), response.errorBody()); } } } catch (IOException e) { Error error = new Error(); error.setError(_context.getString(R.string.toast_error)); apiResponse.setError(error); e.printStackTrace(); } return apiResponse; } private void setError(APIResponse apiResponse, int responseCode, ResponseBody errorBody) { Error error; if (errorBody != null) { try { error = generateError(responseCode, errorBody.string()); } catch (IOException e) { error = new Error(); error.setStatusCode(responseCode); error.setError(_context.getString(R.string.toast_error)); } } else { error = new Error(); error.setStatusCode(responseCode); error.setError(_context.getString(R.string.toast_error)); } apiResponse.setError(error); } public APIResponse getComments(CommentVM.action type, String videoId, String forCommentId, String max_id) { PeertubeService peertubeService = init(); APIResponse apiResponse = new APIResponse(); try { if (type == CommentVM.action.GET_THREAD) { Call commentsCall = peertubeService.getComments(videoId, max_id, count); Response response = commentsCall.execute(); if (response.isSuccessful() && response.body() != null) { apiResponse.setComments(response.body().data); } else { setError(apiResponse, response.code(), response.errorBody()); } } else if (type == CommentVM.action.GET_REPLIES) { Call commentsCall = peertubeService.getReplies(videoId, forCommentId); Response response = commentsCall.execute(); if (response.isSuccessful() && response.body() != null) { apiResponse.setCommentThreadData(response.body()); } else { setError(apiResponse, response.code(), response.errorBody()); } } } catch (IOException e) { Error error = new Error(); error.setError(_context.getString(R.string.toast_error)); apiResponse.setError(error); e.printStackTrace(); } return apiResponse; } /** * Manage comments *synchronously* * * @param type (CommentVM.action * @param videoId String id of the video * @param toCommentId String id of the comment for replies * @param text String text * @return APIResponse */ public APIResponse commentAction(RetrofitPeertubeAPI.ActionType type, String videoId, String toCommentId, String text) { PeertubeService peertubeService = init(); APIResponse apiResponse = new APIResponse(); try { if (type == ActionType.ADD_COMMENT) { Call commentPostedCall = peertubeService.postComment(getToken(), videoId, text); Response response = commentPostedCall.execute(); if (response.isSuccessful() && response.body() != null) { List comments = new ArrayList<>(); comments.add(response.body().getComment()); apiResponse.setComments(comments); } else { setError(apiResponse, response.code(), response.errorBody()); } } else if (type == ActionType.REPLY) { Call commentPostedCall = peertubeService.postReply(getToken(), videoId, toCommentId, text); Response response = commentPostedCall.execute(); if (response.isSuccessful() && response.body() != null) { List comments = new ArrayList<>(); comments.add(response.body().getComment()); apiResponse.setComments(comments); } else { setError(apiResponse, response.code(), response.errorBody()); } } } catch (IOException e) { Error error = new Error(); error.setError(_context.getString(R.string.toast_error)); apiResponse.setError(error); e.printStackTrace(); } return apiResponse; } /** * Retrieves playlist *synchronously* * * @param id String id * @return APIResponse */ public APIResponse getPlayist(String id) { PeertubeService peertubeService = init(); Call playlistCall; playlistCall = peertubeService.getPlaylist(id); APIResponse apiResponse = new APIResponse(); try { Response response = playlistCall.execute(); if (response.isSuccessful()) { List playlists = new ArrayList<>(); playlists.add(response.body()); apiResponse.setPlaylists(playlists); } else { setError(apiResponse, response.code(), response.errorBody()); } } catch (IOException e) { Error error = new Error(); error.setError(_context.getString(R.string.toast_error)); apiResponse.setError(error); e.printStackTrace(); } return apiResponse; } private Error generateError(int responseCode, String message) { Error error = new Error(); error.setStatusCode(responseCode); if (message != null) { error.setError(message); } else { error.setError(_context.getString(R.string.toast_error)); } return error; } /** * Retrieves rating of user on a video *synchronously* * * @param id String id * @return APIResponse */ public APIResponse getRating(String id) { PeertubeService peertubeService = init(); Call rating = peertubeService.getRating(getToken(), id); APIResponse apiResponse = new APIResponse(); try { Response response = rating.execute(); if (response.isSuccessful()) { apiResponse.setRating(response.body()); } else { Error error = new Error(); error.setStatusCode(response.code()); if (response.errorBody() != null) { error.setError(response.errorBody().string()); } else { error.setError(_context.getString(R.string.toast_error)); } } } catch (IOException e) { Error error = new Error(); error.setError(_context.getString(R.string.toast_error)); apiResponse.setError(error); e.printStackTrace(); } return apiResponse; } /** * Retrieves videos *synchronously* * * @param id String id * @return APIResponse */ public APIResponse getVideos(String id, boolean myVideo) { PeertubeService peertubeService = init(); Call video; if (myVideo) { video = peertubeService.getMyVideo(getToken(), id); } else { video = peertubeService.getVideo(id); } APIResponse apiResponse = new APIResponse(); try { Response response = video.execute(); if (response.isSuccessful()) { List videos = new ArrayList<>(); videos.add(response.body()); apiResponse.setPeertubes(videos); } else { Error error = new Error(); error.setStatusCode(response.code()); if (response.errorBody() != null) { error.setError(response.errorBody().string()); } else { error.setError(_context.getString(R.string.toast_error)); } } } catch (IOException e) { Error error = new Error(); error.setError(_context.getString(R.string.toast_error)); apiResponse.setError(error); e.printStackTrace(); } return apiResponse; } public enum DataType { SUBSCRIBER, MUTED, CHANNELS_FOR_ACCOUNT, CHANNEL, MY_CHANNELS } public enum ActionType { FOLLOW, UNFOLLOW, MUTE, UNMUTE, RATEVIDEO, PEERTUBEDELETECOMMENT, PEERTUBE_DELETE_ALL_COMMENT_FOR_ACCOUNT, PEERTUBEDELETEVIDEO, REPORT_VIDEO, REPORT_ACCOUNT, REPORT_COMMENT, DELETE_CHANNEL, ADD_COMMENT, REPLY, } }