From 30be4628ae1cd07fe9d9ed584eb865f874869085 Mon Sep 17 00:00:00 2001 From: ByteHamster Date: Mon, 25 Apr 2022 23:10:18 +0200 Subject: Move feed discovery backends to their own module --- .../activity/OnlineFeedViewActivity.java | 6 +- .../antennapod/adapter/FeedDiscoverAdapter.java | 2 +- .../antennapod/adapter/itunes/ItunesAdapter.java | 4 +- .../antennapod/discovery/CombinedSearcher.java | 118 ------------------- .../antennapod/discovery/FyydPodcastSearcher.java | 53 --------- .../discovery/GpodnetPodcastSearcher.java | 52 --------- .../discovery/ItunesPodcastSearcher.java | 116 ------------------- .../antennapod/discovery/ItunesTopListLoader.java | 103 ----------------- .../discovery/PodcastIndexPodcastSearcher.java | 126 --------------------- .../antennapod/discovery/PodcastSearchResult.java | 110 ------------------ .../antennapod/discovery/PodcastSearcher.java | 14 --- .../discovery/PodcastSearcherRegistry.java | 56 --------- .../antennapod/fragment/AddFeedFragment.java | 8 +- .../antennapod/fragment/DiscoveryFragment.java | 4 +- .../antennapod/fragment/OnlineSearchFragment.java | 6 +- .../fragment/QuickFeedDiscoveryFragment.java | 4 +- .../fragment/gpodnet/GpodnetMainFragment.java | 2 +- 17 files changed, 18 insertions(+), 766 deletions(-) delete mode 100644 app/src/main/java/de/danoeh/antennapod/discovery/CombinedSearcher.java delete mode 100644 app/src/main/java/de/danoeh/antennapod/discovery/FyydPodcastSearcher.java delete mode 100644 app/src/main/java/de/danoeh/antennapod/discovery/GpodnetPodcastSearcher.java delete mode 100644 app/src/main/java/de/danoeh/antennapod/discovery/ItunesPodcastSearcher.java delete mode 100644 app/src/main/java/de/danoeh/antennapod/discovery/ItunesTopListLoader.java delete mode 100644 app/src/main/java/de/danoeh/antennapod/discovery/PodcastIndexPodcastSearcher.java delete mode 100644 app/src/main/java/de/danoeh/antennapod/discovery/PodcastSearchResult.java delete mode 100644 app/src/main/java/de/danoeh/antennapod/discovery/PodcastSearcher.java delete mode 100644 app/src/main/java/de/danoeh/antennapod/discovery/PodcastSearcherRegistry.java (limited to 'app/src') diff --git a/app/src/main/java/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java index 7a26759cc..9e71ac1db 100644 --- a/app/src/main/java/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java +++ b/app/src/main/java/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java @@ -36,8 +36,6 @@ import de.danoeh.antennapod.core.service.download.DownloadService; import de.danoeh.antennapod.core.service.download.DownloadRequestCreator; import de.danoeh.antennapod.core.feed.FeedUrlNotFoundException; import de.danoeh.antennapod.core.util.DownloadErrorLabel; -import de.danoeh.antennapod.discovery.CombinedSearcher; -import de.danoeh.antennapod.discovery.PodcastSearchResult; import de.danoeh.antennapod.event.FeedListUpdateEvent; import de.danoeh.antennapod.event.PlayerStatusEvent; import de.danoeh.antennapod.core.glide.ApGlideSettings; @@ -51,6 +49,9 @@ import de.danoeh.antennapod.core.service.download.HttpDownloader; import de.danoeh.antennapod.core.service.playback.PlaybackService; import de.danoeh.antennapod.core.storage.DBReader; import de.danoeh.antennapod.core.storage.DBWriter; +import de.danoeh.antennapod.net.discovery.CombinedSearcher; +import de.danoeh.antennapod.net.discovery.PodcastSearchResult; +import de.danoeh.antennapod.net.discovery.PodcastSearcherRegistry; import de.danoeh.antennapod.parser.feed.FeedHandler; import de.danoeh.antennapod.parser.feed.FeedHandlerResult; import de.danoeh.antennapod.model.download.DownloadError; @@ -61,7 +62,6 @@ import de.danoeh.antennapod.core.util.syndication.FeedDiscoverer; import de.danoeh.antennapod.core.util.syndication.HtmlToPlainText; import de.danoeh.antennapod.databinding.OnlinefeedviewActivityBinding; import de.danoeh.antennapod.dialog.AuthenticationDialog; -import de.danoeh.antennapod.discovery.PodcastSearcherRegistry; import de.danoeh.antennapod.model.feed.Feed; import de.danoeh.antennapod.model.feed.FeedPreferences; import de.danoeh.antennapod.model.playback.RemoteMedia; diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/FeedDiscoverAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/FeedDiscoverAdapter.java index 66fa79a4e..3628b4bee 100644 --- a/app/src/main/java/de/danoeh/antennapod/adapter/FeedDiscoverAdapter.java +++ b/app/src/main/java/de/danoeh/antennapod/adapter/FeedDiscoverAdapter.java @@ -8,7 +8,7 @@ import com.bumptech.glide.Glide; import com.bumptech.glide.request.RequestOptions; import de.danoeh.antennapod.R; import de.danoeh.antennapod.activity.MainActivity; -import de.danoeh.antennapod.discovery.PodcastSearchResult; +import de.danoeh.antennapod.net.discovery.PodcastSearchResult; import java.lang.ref.WeakReference; import java.util.ArrayList; diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/itunes/ItunesAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/itunes/ItunesAdapter.java index 1968c0b62..40c2029d6 100644 --- a/app/src/main/java/de/danoeh/antennapod/adapter/itunes/ItunesAdapter.java +++ b/app/src/main/java/de/danoeh/antennapod/adapter/itunes/ItunesAdapter.java @@ -1,10 +1,10 @@ package de.danoeh.antennapod.adapter.itunes; import android.content.Context; +import android.widget.ArrayAdapter; import androidx.annotation.NonNull; import android.view.View; import android.view.ViewGroup; -import android.widget.ArrayAdapter; import android.widget.ImageView; import android.widget.TextView; @@ -14,12 +14,12 @@ import com.bumptech.glide.load.engine.DiskCacheStrategy; import com.bumptech.glide.load.resource.bitmap.FitCenter; import com.bumptech.glide.load.resource.bitmap.RoundedCorners; import com.bumptech.glide.request.RequestOptions; -import de.danoeh.antennapod.discovery.PodcastSearchResult; import java.util.List; import de.danoeh.antennapod.R; import de.danoeh.antennapod.activity.MainActivity; +import de.danoeh.antennapod.net.discovery.PodcastSearchResult; public class ItunesAdapter extends ArrayAdapter { /** diff --git a/app/src/main/java/de/danoeh/antennapod/discovery/CombinedSearcher.java b/app/src/main/java/de/danoeh/antennapod/discovery/CombinedSearcher.java deleted file mode 100644 index 6c2a87c12..000000000 --- a/app/src/main/java/de/danoeh/antennapod/discovery/CombinedSearcher.java +++ /dev/null @@ -1,118 +0,0 @@ -package de.danoeh.antennapod.discovery; - -import android.text.TextUtils; -import android.util.Log; -import io.reactivex.Single; -import io.reactivex.SingleOnSubscribe; -import io.reactivex.android.schedulers.AndroidSchedulers; -import io.reactivex.disposables.Disposable; -import io.reactivex.schedulers.Schedulers; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.concurrent.CountDownLatch; - -public class CombinedSearcher implements PodcastSearcher { - private static final String TAG = "CombinedSearcher"; - - public CombinedSearcher() { - } - - public Single> search(String query) { - ArrayList disposables = new ArrayList<>(); - List> singleResults = new ArrayList<>( - Collections.nCopies(PodcastSearcherRegistry.getSearchProviders().size(), null)); - CountDownLatch latch = new CountDownLatch(PodcastSearcherRegistry.getSearchProviders().size()); - for (int i = 0; i < PodcastSearcherRegistry.getSearchProviders().size(); i++) { - PodcastSearcherRegistry.SearcherInfo searchProviderInfo - = PodcastSearcherRegistry.getSearchProviders().get(i); - PodcastSearcher searcher = searchProviderInfo.searcher; - if (searchProviderInfo.weight <= 0.00001f || searcher.getClass() == CombinedSearcher.class) { - latch.countDown(); - continue; - } - final int index = i; - disposables.add(searcher.search(query).subscribe(e -> { - singleResults.set(index, e); - latch.countDown(); - }, throwable -> { - Log.d(TAG, Log.getStackTraceString(throwable)); - latch.countDown(); - } - )); - } - - return Single.create((SingleOnSubscribe>) subscriber -> { - latch.await(); - List results = weightSearchResults(singleResults); - subscriber.onSuccess(results); - }) - .doOnDispose(() -> { - for (Disposable disposable : disposables) { - if (disposable != null) { - disposable.dispose(); - } - } - }) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()); - } - - private List weightSearchResults(List> singleResults) { - HashMap resultRanking = new HashMap<>(); - HashMap urlToResult = new HashMap<>(); - for (int i = 0; i < singleResults.size(); i++) { - float providerPriority = PodcastSearcherRegistry.getSearchProviders().get(i).weight; - List providerResults = singleResults.get(i); - if (providerResults == null) { - continue; - } - for (int position = 0; position < providerResults.size(); position++) { - PodcastSearchResult result = providerResults.get(position); - urlToResult.put(result.feedUrl, result); - - float ranking = 0; - if (resultRanking.containsKey(result.feedUrl)) { - ranking = resultRanking.get(result.feedUrl); - } - ranking += 1.f / (position + 1.f); - resultRanking.put(result.feedUrl, ranking * providerPriority); - } - } - List> sortedResults = new ArrayList<>(resultRanking.entrySet()); - Collections.sort(sortedResults, (o1, o2) -> Double.compare(o2.getValue(), o1.getValue())); - - List results = new ArrayList<>(); - for (Map.Entry res : sortedResults) { - results.add(urlToResult.get(res.getKey())); - } - return results; - } - - @Override - public Single lookupUrl(String url) { - return PodcastSearcherRegistry.lookupUrl(url); - } - - @Override - public boolean urlNeedsLookup(String url) { - return PodcastSearcherRegistry.urlNeedsLookup(url); - } - - @Override - public String getName() { - ArrayList names = new ArrayList<>(); - for (int i = 0; i < PodcastSearcherRegistry.getSearchProviders().size(); i++) { - PodcastSearcherRegistry.SearcherInfo searchProviderInfo - = PodcastSearcherRegistry.getSearchProviders().get(i); - PodcastSearcher searcher = searchProviderInfo.searcher; - if (searchProviderInfo.weight > 0.00001f && searcher.getClass() != CombinedSearcher.class) { - names.add(searcher.getName()); - } - } - return TextUtils.join(", ", names); - } -} diff --git a/app/src/main/java/de/danoeh/antennapod/discovery/FyydPodcastSearcher.java b/app/src/main/java/de/danoeh/antennapod/discovery/FyydPodcastSearcher.java deleted file mode 100644 index 5a93e6530..000000000 --- a/app/src/main/java/de/danoeh/antennapod/discovery/FyydPodcastSearcher.java +++ /dev/null @@ -1,53 +0,0 @@ -package de.danoeh.antennapod.discovery; - -import de.danoeh.antennapod.core.service.download.AntennapodHttpClient; -import de.mfietz.fyydlin.FyydClient; -import de.mfietz.fyydlin.FyydResponse; -import de.mfietz.fyydlin.SearchHit; -import io.reactivex.Single; -import io.reactivex.SingleOnSubscribe; -import io.reactivex.android.schedulers.AndroidSchedulers; -import io.reactivex.schedulers.Schedulers; - -import java.util.ArrayList; -import java.util.List; - -public class FyydPodcastSearcher implements PodcastSearcher { - private final FyydClient client = new FyydClient(AntennapodHttpClient.getHttpClient()); - - public Single> search(String query) { - return Single.create((SingleOnSubscribe>) subscriber -> { - FyydResponse response = client.searchPodcasts(query, 10) - .subscribeOn(Schedulers.io()) - .blockingGet(); - - ArrayList searchResults = new ArrayList<>(); - - if (!response.getData().isEmpty()) { - for (SearchHit searchHit : response.getData()) { - PodcastSearchResult podcast = PodcastSearchResult.fromFyyd(searchHit); - searchResults.add(podcast); - } - } - - subscriber.onSuccess(searchResults); - }) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()); - } - - @Override - public Single lookupUrl(String url) { - return Single.just(url); - } - - @Override - public boolean urlNeedsLookup(String url) { - return false; - } - - @Override - public String getName() { - return "Fyyd"; - } -} diff --git a/app/src/main/java/de/danoeh/antennapod/discovery/GpodnetPodcastSearcher.java b/app/src/main/java/de/danoeh/antennapod/discovery/GpodnetPodcastSearcher.java deleted file mode 100644 index 340783208..000000000 --- a/app/src/main/java/de/danoeh/antennapod/discovery/GpodnetPodcastSearcher.java +++ /dev/null @@ -1,52 +0,0 @@ -package de.danoeh.antennapod.discovery; - -import de.danoeh.antennapod.core.sync.SynchronizationCredentials; -import de.danoeh.antennapod.core.service.download.AntennapodHttpClient; -import de.danoeh.antennapod.net.sync.gpoddernet.GpodnetService; -import de.danoeh.antennapod.net.sync.gpoddernet.GpodnetServiceException; -import de.danoeh.antennapod.net.sync.gpoddernet.model.GpodnetPodcast; -import io.reactivex.Single; -import io.reactivex.SingleOnSubscribe; -import io.reactivex.android.schedulers.AndroidSchedulers; -import io.reactivex.schedulers.Schedulers; - -import java.util.ArrayList; -import java.util.List; - -public class GpodnetPodcastSearcher implements PodcastSearcher { - public Single> search(String query) { - return Single.create((SingleOnSubscribe>) subscriber -> { - try { - GpodnetService service = new GpodnetService(AntennapodHttpClient.getHttpClient(), - SynchronizationCredentials.getHosturl(), SynchronizationCredentials.getDeviceID(), - SynchronizationCredentials.getUsername(), SynchronizationCredentials.getPassword()); - List gpodnetPodcasts = service.searchPodcasts(query, 0); - List results = new ArrayList<>(); - for (GpodnetPodcast podcast : gpodnetPodcasts) { - results.add(PodcastSearchResult.fromGpodder(podcast)); - } - subscriber.onSuccess(results); - } catch (GpodnetServiceException e) { - e.printStackTrace(); - subscriber.onError(e); - } - }) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()); - } - - @Override - public Single lookupUrl(String url) { - return Single.just(url); - } - - @Override - public boolean urlNeedsLookup(String url) { - return false; - } - - @Override - public String getName() { - return "Gpodder.net"; - } -} diff --git a/app/src/main/java/de/danoeh/antennapod/discovery/ItunesPodcastSearcher.java b/app/src/main/java/de/danoeh/antennapod/discovery/ItunesPodcastSearcher.java deleted file mode 100644 index 81ce77ef8..000000000 --- a/app/src/main/java/de/danoeh/antennapod/discovery/ItunesPodcastSearcher.java +++ /dev/null @@ -1,116 +0,0 @@ -package de.danoeh.antennapod.discovery; - -import de.danoeh.antennapod.core.feed.FeedUrlNotFoundException; -import de.danoeh.antennapod.core.service.download.AntennapodHttpClient; -import io.reactivex.Single; -import io.reactivex.SingleOnSubscribe; -import io.reactivex.android.schedulers.AndroidSchedulers; -import io.reactivex.schedulers.Schedulers; -import okhttp3.OkHttpClient; -import okhttp3.Request; -import okhttp3.Response; -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; - -import java.io.IOException; -import java.io.UnsupportedEncodingException; -import java.net.URLEncoder; -import java.util.ArrayList; -import java.util.List; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -public class ItunesPodcastSearcher implements PodcastSearcher { - private static final String ITUNES_API_URL = "https://itunes.apple.com/search?media=podcast&term=%s"; - private static final String PATTERN_BY_ID = ".*/podcasts\\.apple\\.com/.*/podcast/.*/id(\\d+).*"; - - public ItunesPodcastSearcher() { - } - - @Override - public Single> search(String query) { - return Single.create((SingleOnSubscribe>) subscriber -> { - String encodedQuery; - try { - encodedQuery = URLEncoder.encode(query, "UTF-8"); - } catch (UnsupportedEncodingException e) { - // this won't ever be thrown - encodedQuery = query; - } - - String formattedUrl = String.format(ITUNES_API_URL, encodedQuery); - - OkHttpClient client = AntennapodHttpClient.getHttpClient(); - Request.Builder httpReq = new Request.Builder() - .url(formattedUrl); - List podcasts = new ArrayList<>(); - try { - Response response = client.newCall(httpReq.build()).execute(); - - if (response.isSuccessful()) { - String resultString = response.body().string(); - JSONObject result = new JSONObject(resultString); - JSONArray j = result.getJSONArray("results"); - - for (int i = 0; i < j.length(); i++) { - JSONObject podcastJson = j.getJSONObject(i); - PodcastSearchResult podcast = PodcastSearchResult.fromItunes(podcastJson); - if (podcast.feedUrl != null) { - podcasts.add(podcast); - } - } - } else { - subscriber.onError(new IOException(response.toString())); - } - } catch (IOException | JSONException e) { - subscriber.onError(e); - } - subscriber.onSuccess(podcasts); - }) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()); - } - - @Override - public Single lookupUrl(String url) { - Pattern pattern = Pattern.compile(PATTERN_BY_ID); - Matcher matcher = pattern.matcher(url); - final String lookupUrl = matcher.find() ? ("https://itunes.apple.com/lookup?id=" + matcher.group(1)) : url; - return Single.create(emitter -> { - OkHttpClient client = AntennapodHttpClient.getHttpClient(); - Request.Builder httpReq = new Request.Builder().url(lookupUrl); - try { - Response response = client.newCall(httpReq.build()).execute(); - if (response.isSuccessful()) { - String resultString = response.body().string(); - JSONObject result = new JSONObject(resultString); - JSONObject results = result.getJSONArray("results").getJSONObject(0); - String feedUrlName = "feedUrl"; - if (!results.has(feedUrlName)) { - String artistName = results.getString("artistName"); - String trackName = results.getString("trackName"); - emitter.onError(new FeedUrlNotFoundException(artistName, trackName)); - return; - } - String feedUrl = results.getString(feedUrlName); - emitter.onSuccess(feedUrl); - } else { - emitter.onError(new IOException(response.toString())); - } - } catch (IOException | JSONException e) { - emitter.onError(e); - } - }); - } - - @Override - public boolean urlNeedsLookup(String url) { - return url.contains("itunes.apple.com") || url.matches(PATTERN_BY_ID); - } - - @Override - public String getName() { - return "iTunes"; - } -} diff --git a/app/src/main/java/de/danoeh/antennapod/discovery/ItunesTopListLoader.java b/app/src/main/java/de/danoeh/antennapod/discovery/ItunesTopListLoader.java deleted file mode 100644 index e4135fcaa..000000000 --- a/app/src/main/java/de/danoeh/antennapod/discovery/ItunesTopListLoader.java +++ /dev/null @@ -1,103 +0,0 @@ -package de.danoeh.antennapod.discovery; - -import android.content.Context; -import android.util.Log; - -import de.danoeh.antennapod.R; -import de.danoeh.antennapod.core.service.download.AntennapodHttpClient; -import io.reactivex.Single; -import io.reactivex.SingleOnSubscribe; -import io.reactivex.android.schedulers.AndroidSchedulers; -import io.reactivex.schedulers.Schedulers; -import okhttp3.CacheControl; -import okhttp3.OkHttpClient; -import okhttp3.Request; -import okhttp3.Response; -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; -import java.util.Locale; -import java.util.concurrent.TimeUnit; - -public class ItunesTopListLoader { - private static final String TAG = "ITunesTopListLoader"; - private final Context context; - public static final String PREF_KEY_COUNTRY_CODE = "country_code"; - public static final String PREFS = "CountryRegionPrefs"; - public static final String DISCOVER_HIDE_FAKE_COUNTRY_CODE = "00"; - public static final String COUNTRY_CODE_UNSET = "99"; - - public ItunesTopListLoader(Context context) { - this.context = context; - } - - public Single> loadToplist(String country, int limit) { - return Single.create((SingleOnSubscribe>) emitter -> { - OkHttpClient client = AntennapodHttpClient.getHttpClient(); - String feedString; - String loadCountry = country; - if (COUNTRY_CODE_UNSET.equals(country)) { - loadCountry = Locale.getDefault().getCountry(); - } - try { - feedString = getTopListFeed(client, loadCountry, limit); - } catch (IOException e) { - if (COUNTRY_CODE_UNSET.equals(country)) { - feedString = getTopListFeed(client, "US", limit); - } else { - emitter.onError(e); - return; - } - } - - List podcasts = parseFeed(feedString); - emitter.onSuccess(podcasts); - }) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()); - } - - private String getTopListFeed(OkHttpClient client, String country, int limit) throws IOException { - String url = "https://itunes.apple.com/%s/rss/toppodcasts/limit=" + limit + "/explicit=true/json"; - Log.d(TAG, "Feed URL " + String.format(url, country)); - Request.Builder httpReq = new Request.Builder() - .cacheControl(new CacheControl.Builder().maxStale(1, TimeUnit.DAYS).build()) - .url(String.format(url, country)); - - try (Response response = client.newCall(httpReq.build()).execute()) { - if (response.isSuccessful()) { - return response.body().string(); - } - if (response.code() == 400) { - throw new IOException("iTunes does not have data for the selected country."); - } - String prefix = context.getString(R.string.error_msg_prefix); - throw new IOException(prefix + response); - } - } - - private List parseFeed(String jsonString) throws JSONException { - JSONObject result = new JSONObject(jsonString); - JSONObject feed; - JSONArray entries; - try { - feed = result.getJSONObject("feed"); - entries = feed.getJSONArray("entry"); - } catch (JSONException e) { - return new ArrayList<>(); - } - - List results = new ArrayList<>(); - for (int i = 0; i < entries.length(); i++) { - JSONObject json = entries.getJSONObject(i); - results.add(PodcastSearchResult.fromItunesToplist(json)); - } - - return results; - } - -} diff --git a/app/src/main/java/de/danoeh/antennapod/discovery/PodcastIndexPodcastSearcher.java b/app/src/main/java/de/danoeh/antennapod/discovery/PodcastIndexPodcastSearcher.java deleted file mode 100644 index c8e5dc4ef..000000000 --- a/app/src/main/java/de/danoeh/antennapod/discovery/PodcastIndexPodcastSearcher.java +++ /dev/null @@ -1,126 +0,0 @@ -package de.danoeh.antennapod.discovery; - -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; - -import java.io.IOException; -import java.io.UnsupportedEncodingException; -import java.net.URLEncoder; -import java.security.MessageDigest; -import java.util.ArrayList; -import java.util.Calendar; -import java.util.Date; -import java.util.List; -import java.util.Locale; -import java.util.TimeZone; - -import de.danoeh.antennapod.BuildConfig; -import de.danoeh.antennapod.core.ClientConfig; -import de.danoeh.antennapod.core.service.download.AntennapodHttpClient; -import io.reactivex.Single; -import io.reactivex.SingleOnSubscribe; -import io.reactivex.android.schedulers.AndroidSchedulers; -import io.reactivex.schedulers.Schedulers; -import okhttp3.OkHttpClient; -import okhttp3.Request; -import okhttp3.Response; - -public class PodcastIndexPodcastSearcher implements PodcastSearcher { - private static final String PODCASTINDEX_API_URL = "https://api.podcastindex.org/api/1.0/search/byterm?q=%s"; - - public PodcastIndexPodcastSearcher() { - } - - @Override - public Single> search(String query) { - return Single.create((SingleOnSubscribe>) subscriber -> { - - Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("UTC")); - calendar.clear(); - Date now = new Date(); - calendar.setTime(now); - long secondsSinceEpoch = calendar.getTimeInMillis() / 1000L; - String apiHeaderTime = String.valueOf(secondsSinceEpoch); - String data4Hash = BuildConfig.PODCASTINDEX_API_KEY + BuildConfig.PODCASTINDEX_API_SECRET + apiHeaderTime; - String hashString = sha1(data4Hash); - - String encodedQuery; - try { - encodedQuery = URLEncoder.encode(query, "UTF-8"); - } catch (UnsupportedEncodingException e) { - // this won't ever be thrown - encodedQuery = query; - } - - String formattedUrl = String.format(PODCASTINDEX_API_URL, encodedQuery); - - OkHttpClient client = AntennapodHttpClient.getHttpClient(); - Request.Builder httpReq = new Request.Builder() - .addHeader("X-Auth-Date", apiHeaderTime) - .addHeader("X-Auth-Key", BuildConfig.PODCASTINDEX_API_KEY) - .addHeader("Authorization", hashString) - .addHeader("User-Agent", ClientConfig.USER_AGENT) - .url(formattedUrl); - List podcasts = new ArrayList<>(); - try { - Response response = client.newCall(httpReq.build()).execute(); - - if (response.isSuccessful()) { - String resultString = response.body().string(); - JSONObject result = new JSONObject(resultString); - JSONArray j = result.getJSONArray("feeds"); - - for (int i = 0; i < j.length(); i++) { - JSONObject podcastJson = j.getJSONObject(i); - PodcastSearchResult podcast = PodcastSearchResult.fromPodcastIndex(podcastJson); - if (podcast.feedUrl != null) { - podcasts.add(podcast); - } - } - } else { - subscriber.onError(new IOException(response.toString())); - } - } catch (IOException | JSONException e) { - subscriber.onError(e); - } - subscriber.onSuccess(podcasts); - }) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()); - } - - @Override - public Single lookupUrl(String url) { - return Single.just(url); - } - - @Override - public boolean urlNeedsLookup(String url) { - return false; - } - - @Override - public String getName() { - return "Podcastindex.org"; - } - - private static String sha1(String clearString) { - try { - MessageDigest messageDigest = MessageDigest.getInstance("SHA-1"); - messageDigest.update(clearString.getBytes("UTF-8")); - return toHex(messageDigest.digest()); - } catch (Exception ignored) { - ignored.printStackTrace(); - return null; - } - } - - private static String toHex(byte[] bytes) { - StringBuilder buffer = new StringBuilder(); - for (byte b : bytes) { - buffer.append(String.format(Locale.getDefault(), "%02x", b)); - } - return buffer.toString(); - } -} diff --git a/app/src/main/java/de/danoeh/antennapod/discovery/PodcastSearchResult.java b/app/src/main/java/de/danoeh/antennapod/discovery/PodcastSearchResult.java deleted file mode 100644 index c8096e9c5..000000000 --- a/app/src/main/java/de/danoeh/antennapod/discovery/PodcastSearchResult.java +++ /dev/null @@ -1,110 +0,0 @@ -package de.danoeh.antennapod.discovery; - -import androidx.annotation.Nullable; -import de.danoeh.antennapod.net.sync.gpoddernet.model.GpodnetPodcast; -import de.mfietz.fyydlin.SearchHit; -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; - -public class PodcastSearchResult { - - /** - * The name of the podcast - */ - public final String title; - - /** - * URL of the podcast image - */ - @Nullable - public final String imageUrl; - /** - * URL of the podcast feed - */ - @Nullable - public final String feedUrl; - - /** - * artistName of the podcast feed - */ - @Nullable - public final String author; - - - private PodcastSearchResult(String title, @Nullable String imageUrl, @Nullable String feedUrl, @Nullable String author) { - this.title = title; - this.imageUrl = imageUrl; - this.feedUrl = feedUrl; - this.author = author; - } - - public static PodcastSearchResult dummy() { - return new PodcastSearchResult("", "", "", ""); - } - - /** - * Constructs a Podcast instance from a iTunes search result - * - * @param json object holding the podcast information - * @throws JSONException - */ - public static PodcastSearchResult fromItunes(JSONObject json) { - String title = json.optString("collectionName", ""); - String imageUrl = json.optString("artworkUrl100", null); - String feedUrl = json.optString("feedUrl", null); - String author = json.optString("artistName", null); - return new PodcastSearchResult(title, imageUrl, feedUrl, author); - } - - /** - * Constructs a Podcast instance from iTunes toplist entry - * - * @param json object holding the podcast information - * @throws JSONException - */ - public static PodcastSearchResult fromItunesToplist(JSONObject json) throws JSONException { - String title = json.getJSONObject("title").getString("label"); - String imageUrl = null; - JSONArray images = json.getJSONArray("im:image"); - for(int i=0; imageUrl == null && i < images.length(); i++) { - JSONObject image = images.getJSONObject(i); - String height = image.getJSONObject("attributes").getString("height"); - if(Integer.parseInt(height) >= 100) { - imageUrl = image.getString("label"); - } - } - String feedUrl = "https://itunes.apple.com/lookup?id=" + - json.getJSONObject("id").getJSONObject("attributes").getString("im:id"); - - String author = null; - try { - author = json.getJSONObject("im:artist").getString("label"); - } catch (Exception e) { - // Some feeds have empty artist - } - return new PodcastSearchResult(title, imageUrl, feedUrl, author); - } - - public static PodcastSearchResult fromFyyd(SearchHit searchHit) { - return new PodcastSearchResult(searchHit.getTitle(), - searchHit.getThumbImageURL(), - searchHit.getXmlUrl(), - searchHit.getAuthor()); - } - - public static PodcastSearchResult fromGpodder(GpodnetPodcast searchHit) { - return new PodcastSearchResult(searchHit.getTitle(), - searchHit.getLogoUrl(), - searchHit.getUrl(), - searchHit.getAuthor()); - } - - public static PodcastSearchResult fromPodcastIndex(JSONObject json) { - String title = json.optString("title", ""); - String imageUrl = json.optString("image", null); - String feedUrl = json.optString("url", null); - String author = json.optString("author", null); - return new PodcastSearchResult(title, imageUrl, feedUrl, author); - } -} diff --git a/app/src/main/java/de/danoeh/antennapod/discovery/PodcastSearcher.java b/app/src/main/java/de/danoeh/antennapod/discovery/PodcastSearcher.java deleted file mode 100644 index 8fbc8c76b..000000000 --- a/app/src/main/java/de/danoeh/antennapod/discovery/PodcastSearcher.java +++ /dev/null @@ -1,14 +0,0 @@ -package de.danoeh.antennapod.discovery; - -import io.reactivex.Single; -import java.util.List; - -public interface PodcastSearcher { - Single> search(String query); - - Single lookupUrl(String resultUrl); - - boolean urlNeedsLookup(String resultUrl); - - String getName(); -} diff --git a/app/src/main/java/de/danoeh/antennapod/discovery/PodcastSearcherRegistry.java b/app/src/main/java/de/danoeh/antennapod/discovery/PodcastSearcherRegistry.java deleted file mode 100644 index dfea627df..000000000 --- a/app/src/main/java/de/danoeh/antennapod/discovery/PodcastSearcherRegistry.java +++ /dev/null @@ -1,56 +0,0 @@ -package de.danoeh.antennapod.discovery; - - -import io.reactivex.Single; - -import java.util.ArrayList; -import java.util.List; - -public class PodcastSearcherRegistry { - private static List searchProviders; - - private PodcastSearcherRegistry() { - } - - public static synchronized List getSearchProviders() { - if (searchProviders == null) { - searchProviders = new ArrayList<>(); - searchProviders.add(new SearcherInfo(new CombinedSearcher(), 1.0f)); - searchProviders.add(new SearcherInfo(new GpodnetPodcastSearcher(), 0.0f)); - searchProviders.add(new SearcherInfo(new FyydPodcastSearcher(), 1.0f)); - searchProviders.add(new SearcherInfo(new ItunesPodcastSearcher(), 1.0f)); - searchProviders.add(new SearcherInfo(new PodcastIndexPodcastSearcher(), 1.0f)); - } - return searchProviders; - } - - public static Single lookupUrl(String url) { - for (PodcastSearcherRegistry.SearcherInfo searchProviderInfo : getSearchProviders()) { - if (searchProviderInfo.searcher.getClass() != CombinedSearcher.class - && searchProviderInfo.searcher.urlNeedsLookup(url)) { - return searchProviderInfo.searcher.lookupUrl(url); - } - } - return Single.just(url); - } - - public static boolean urlNeedsLookup(String url) { - for (PodcastSearcherRegistry.SearcherInfo searchProviderInfo : getSearchProviders()) { - if (searchProviderInfo.searcher.getClass() != CombinedSearcher.class - && searchProviderInfo.searcher.urlNeedsLookup(url)) { - return true; - } - } - return false; - } - - public static class SearcherInfo { - public final PodcastSearcher searcher; - public final float weight; - - public SearcherInfo(PodcastSearcher searcher, float weight) { - this.searcher = searcher; - this.weight = weight; - } - } -} diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/AddFeedFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/AddFeedFragment.java index ee56bb9f9..d2a2e6de8 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/AddFeedFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/AddFeedFragment.java @@ -35,11 +35,11 @@ import de.danoeh.antennapod.core.storage.DBTasks; import de.danoeh.antennapod.model.feed.SortOrder; import de.danoeh.antennapod.databinding.AddfeedBinding; import de.danoeh.antennapod.databinding.EditTextDialogBinding; -import de.danoeh.antennapod.discovery.CombinedSearcher; -import de.danoeh.antennapod.discovery.FyydPodcastSearcher; -import de.danoeh.antennapod.discovery.ItunesPodcastSearcher; -import de.danoeh.antennapod.discovery.PodcastIndexPodcastSearcher; import de.danoeh.antennapod.fragment.gpodnet.GpodnetMainFragment; +import de.danoeh.antennapod.net.discovery.CombinedSearcher; +import de.danoeh.antennapod.net.discovery.FyydPodcastSearcher; +import de.danoeh.antennapod.net.discovery.ItunesPodcastSearcher; +import de.danoeh.antennapod.net.discovery.PodcastIndexPodcastSearcher; import io.reactivex.Observable; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.schedulers.Schedulers; diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/DiscoveryFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/DiscoveryFragment.java index 230a0ce0d..274fc5878 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/DiscoveryFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/DiscoveryFragment.java @@ -18,14 +18,14 @@ import androidx.annotation.NonNull; import androidx.appcompat.widget.Toolbar; import androidx.fragment.app.Fragment; +import de.danoeh.antennapod.net.discovery.ItunesTopListLoader; +import de.danoeh.antennapod.net.discovery.PodcastSearchResult; import org.greenrobot.eventbus.EventBus; import de.danoeh.antennapod.R; import de.danoeh.antennapod.activity.OnlineFeedViewActivity; import de.danoeh.antennapod.adapter.itunes.ItunesAdapter; import de.danoeh.antennapod.event.DiscoveryDefaultUpdateEvent; -import de.danoeh.antennapod.discovery.ItunesTopListLoader; -import de.danoeh.antennapod.discovery.PodcastSearchResult; import io.reactivex.disposables.Disposable; import java.util.ArrayList; diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/OnlineSearchFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/OnlineSearchFragment.java index f3080f655..2fda74296 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/OnlineSearchFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/OnlineSearchFragment.java @@ -23,9 +23,9 @@ import de.danoeh.antennapod.R; import de.danoeh.antennapod.activity.MainActivity; import de.danoeh.antennapod.activity.OnlineFeedViewActivity; import de.danoeh.antennapod.adapter.itunes.ItunesAdapter; -import de.danoeh.antennapod.discovery.PodcastSearchResult; -import de.danoeh.antennapod.discovery.PodcastSearcher; -import de.danoeh.antennapod.discovery.PodcastSearcherRegistry; +import de.danoeh.antennapod.net.discovery.PodcastSearchResult; +import de.danoeh.antennapod.net.discovery.PodcastSearcher; +import de.danoeh.antennapod.net.discovery.PodcastSearcherRegistry; import io.reactivex.disposables.Disposable; import java.util.ArrayList; diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/QuickFeedDiscoveryFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/QuickFeedDiscoveryFragment.java index f76b902cd..439f8a2cc 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/QuickFeedDiscoveryFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/QuickFeedDiscoveryFragment.java @@ -17,6 +17,8 @@ import android.widget.LinearLayout; import android.widget.ProgressBar; import android.widget.TextView; +import de.danoeh.antennapod.net.discovery.ItunesTopListLoader; +import de.danoeh.antennapod.net.discovery.PodcastSearchResult; import org.greenrobot.eventbus.EventBus; import org.greenrobot.eventbus.Subscribe; import org.greenrobot.eventbus.ThreadMode; @@ -26,8 +28,6 @@ import de.danoeh.antennapod.activity.MainActivity; import de.danoeh.antennapod.activity.OnlineFeedViewActivity; import de.danoeh.antennapod.adapter.FeedDiscoverAdapter; import de.danoeh.antennapod.event.DiscoveryDefaultUpdateEvent; -import de.danoeh.antennapod.discovery.ItunesTopListLoader; -import de.danoeh.antennapod.discovery.PodcastSearchResult; import io.reactivex.disposables.Disposable; import java.util.ArrayList; diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/GpodnetMainFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/GpodnetMainFragment.java index 2ec30b8c3..fc2f1fa47 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/GpodnetMainFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/GpodnetMainFragment.java @@ -19,8 +19,8 @@ import com.google.android.material.tabs.TabLayoutMediator; import de.danoeh.antennapod.R; import de.danoeh.antennapod.activity.MainActivity; -import de.danoeh.antennapod.discovery.GpodnetPodcastSearcher; import de.danoeh.antennapod.fragment.OnlineSearchFragment; +import de.danoeh.antennapod.net.discovery.GpodnetPodcastSearcher; /** * Main navigation hub for gpodder.net podcast directory -- cgit v1.2.3