mirror of
https://github.com/MarginaliaSearch/MarginaliaSearch.git
synced 2025-02-23 13:09:00 +00:00
(search) Add experimental OPML-export function for feed subscriptions
This commit is contained in:
parent
ab5c30ad51
commit
84f55b84ff
@ -1,6 +1,7 @@
|
|||||||
package nu.marginalia.search;
|
package nu.marginalia.search;
|
||||||
|
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
|
import io.jooby.Jooby;
|
||||||
import io.prometheus.client.Counter;
|
import io.prometheus.client.Counter;
|
||||||
import io.prometheus.client.Histogram;
|
import io.prometheus.client.Histogram;
|
||||||
import nu.marginalia.WebsiteUrl;
|
import nu.marginalia.WebsiteUrl;
|
||||||
@ -18,6 +19,7 @@ public class SearchService extends JoobyService {
|
|||||||
|
|
||||||
private final WebsiteUrl websiteUrl;
|
private final WebsiteUrl websiteUrl;
|
||||||
private final StaticResources staticResources;
|
private final StaticResources staticResources;
|
||||||
|
private final SearchSiteSubscriptionService siteSubscriptionService;
|
||||||
|
|
||||||
private static final Logger logger = LoggerFactory.getLogger(SearchService.class);
|
private static final Logger logger = LoggerFactory.getLogger(SearchService.class);
|
||||||
private static final Histogram wmsa_search_service_request_time = Histogram.build()
|
private static final Histogram wmsa_search_service_request_time = Histogram.build()
|
||||||
@ -38,6 +40,7 @@ public class SearchService extends JoobyService {
|
|||||||
StaticResources staticResources,
|
StaticResources staticResources,
|
||||||
SearchFrontPageService frontPageService,
|
SearchFrontPageService frontPageService,
|
||||||
SearchAddToCrawlQueueService addToCrawlQueueService,
|
SearchAddToCrawlQueueService addToCrawlQueueService,
|
||||||
|
SearchSiteSubscriptionService siteSubscriptionService,
|
||||||
SearchSiteInfoService siteInfoService,
|
SearchSiteInfoService siteInfoService,
|
||||||
SearchCrosstalkService crosstalkService,
|
SearchCrosstalkService crosstalkService,
|
||||||
SearchBrowseService searchBrowseService,
|
SearchBrowseService searchBrowseService,
|
||||||
@ -57,6 +60,14 @@ public class SearchService extends JoobyService {
|
|||||||
this.websiteUrl = websiteUrl;
|
this.websiteUrl = websiteUrl;
|
||||||
this.staticResources = staticResources;
|
this.staticResources = staticResources;
|
||||||
|
|
||||||
|
this.siteSubscriptionService = siteSubscriptionService;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void startJooby(Jooby jooby) {
|
||||||
|
super.startJooby(jooby);
|
||||||
|
|
||||||
|
jooby.get("/export-opml", siteSubscriptionService::exportOpml);
|
||||||
}
|
}
|
||||||
//
|
//
|
||||||
// SearchServiceMetrics.get("/search", searchQueryService::pathSearch);
|
// SearchServiceMetrics.get("/search", searchQueryService::pathSearch);
|
||||||
|
@ -4,6 +4,8 @@ import com.google.inject.Inject;
|
|||||||
import io.jooby.Context;
|
import io.jooby.Context;
|
||||||
import io.jooby.Cookie;
|
import io.jooby.Cookie;
|
||||||
import io.jooby.Value;
|
import io.jooby.Value;
|
||||||
|
import nu.marginalia.api.feeds.FeedsClient;
|
||||||
|
import nu.marginalia.api.feeds.RpcFeed;
|
||||||
import nu.marginalia.db.DbDomainQueries;
|
import nu.marginalia.db.DbDomainQueries;
|
||||||
import nu.marginalia.model.EdgeDomain;
|
import nu.marginalia.model.EdgeDomain;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
@ -12,19 +14,25 @@ import org.slf4j.LoggerFactory;
|
|||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.nio.IntBuffer;
|
import java.nio.IntBuffer;
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.time.ZoneOffset;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
import java.util.Base64;
|
import java.util.Base64;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.NoSuchElementException;
|
import java.util.NoSuchElementException;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
|
|
||||||
public class SearchSiteSubscriptionService {
|
public class SearchSiteSubscriptionService {
|
||||||
private final DbDomainQueries dbDomainQueries;
|
private final DbDomainQueries dbDomainQueries;
|
||||||
|
private final FeedsClient feedsClient;
|
||||||
|
|
||||||
private static final Logger logger = LoggerFactory.getLogger(SearchSiteSubscriptionService.class);
|
private static final Logger logger = LoggerFactory.getLogger(SearchSiteSubscriptionService.class);
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public SearchSiteSubscriptionService(DbDomainQueries dbDomainQueries) {
|
public SearchSiteSubscriptionService(DbDomainQueries dbDomainQueries, FeedsClient feedsClient) {
|
||||||
this.dbDomainQueries = dbDomainQueries;
|
this.dbDomainQueries = dbDomainQueries;
|
||||||
|
this.feedsClient = feedsClient;
|
||||||
}
|
}
|
||||||
|
|
||||||
public HashSet<Integer> getSubscriptions(Context context) {
|
public HashSet<Integer> getSubscriptions(Context context) {
|
||||||
@ -90,4 +98,32 @@ public class SearchSiteSubscriptionService {
|
|||||||
|
|
||||||
putSubscriptions(context, subscriptions);
|
putSubscriptions(context, subscriptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Object exportOpml(Context ctx) throws ExecutionException, InterruptedException {
|
||||||
|
ctx.setResponseType("text/xml.opml");
|
||||||
|
ctx.setResponseHeader("Content-Disposition", "attachment; filename=feeds.opml");
|
||||||
|
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.append("<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n");
|
||||||
|
sb.append("<opml version=\"2.0\">\n");
|
||||||
|
sb.append("<!-- This is an OPM file that can be imported into many feed readers. See https://opml.org/ for spec. -->\n");
|
||||||
|
sb.append("<head>\n");
|
||||||
|
sb.append("<title>Marginalia Subscriptions</title>\n");
|
||||||
|
sb.append("<dateCreated>").append(LocalDateTime.now().atOffset(ZoneOffset.UTC).format(DateTimeFormatter.RFC_1123_DATE_TIME)).append("</dateCreated>\n");
|
||||||
|
sb.append("</head>\n");
|
||||||
|
|
||||||
|
sb.append("<body>\n");
|
||||||
|
for (int domainId : getSubscriptions(ctx)) {
|
||||||
|
RpcFeed feed = feedsClient.getFeed(domainId).get();
|
||||||
|
sb.append("<outline title=\"")
|
||||||
|
.append(feed.getDomain())
|
||||||
|
.append("\" htmlUrl=\"")
|
||||||
|
.append(new EdgeDomain(feed.getDomain()).toRootUrlHttps().toString())
|
||||||
|
.append("\" xmlUrl=\"").append(feed.getFeedUrl()).append("\"/>\n");
|
||||||
|
}
|
||||||
|
sb.append("</body>\n");
|
||||||
|
sb.append("</opml>");
|
||||||
|
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -81,7 +81,14 @@
|
|||||||
</div>
|
</div>
|
||||||
@else
|
@else
|
||||||
<div class="max-w-7xl mx-auto flex flex-col space-y-4 fill-w m-4">
|
<div class="max-w-7xl mx-auto flex flex-col space-y-4 fill-w m-4">
|
||||||
<div class="my-4 text-black dark:text-white font-serif text-xl mx-8">Subscriptions</div>
|
<div class="my-4 text-black dark:text-white font-serif text-xl mx-8 place-items-baseline flex">
|
||||||
|
Subscriptions
|
||||||
|
<div class="grow"></div>
|
||||||
|
<a class="text-sm font-sans" href="/export-opml">
|
||||||
|
<i class="fas fa-download mr-2"></i>
|
||||||
|
Export as OPML
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
@for (NewsItemCluster cluster : model.news())
|
@for (NewsItemCluster cluster : model.news())
|
||||||
!{NewsItem item = cluster.first();}
|
!{NewsItem item = cluster.first();}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user