From 97aeb049d695ee406626fd910573e29088c04d92 Mon Sep 17 00:00:00 2001 From: Thomas Rademaker Date: Wed, 18 Mar 2026 13:57:11 -0400 Subject: [PATCH] add additional podcast index apis --- Sources/EffemKit/EffemAPI.swift | 36 ++++++++++++++----- Sources/EffemKit/EffemService.swift | 36 +++++++++++++++---- .../EffemKit/Models/PodcastSearchResult.swift | 23 ++++++++++++ 3 files changed, 80 insertions(+), 15 deletions(-) diff --git a/Sources/EffemKit/EffemAPI.swift b/Sources/EffemKit/EffemAPI.swift index def3f07..80c9bda 100644 --- a/Sources/EffemKit/EffemAPI.swift +++ b/Sources/EffemKit/EffemAPI.swift @@ -21,13 +21,15 @@ enum EffemAPI { // MARK: - Podcast Index Proxy (cached by AppView) - case searchPodcasts(query: String, max: Int) + case searchPodcasts(query: String, max: Int, searchType: String?) case searchEpisodes(query: String, max: Int) - case getPodcast(feedId: Int) - case getEpisodes(feedId: Int, max: Int) + case getPodcast(feedId: Int?, itunesId: Int?) + case getEpisodes(feedId: Int?, feedUrl: String?, max: Int) case getEpisode(episodeId: Int) case getTrending(max: Int, lang: String?, cat: String?) case getCategories + case getRecentEpisodes(max: Int) + case getStats } extension EffemAPI: EndpointType { @@ -64,6 +66,8 @@ extension EffemAPI: EndpointType { case .getEpisode: "/xrpc/xyz.effem.podcast.getEpisode" case .getTrending: "/xrpc/xyz.effem.podcast.getTrending" case .getCategories: "/xrpc/xyz.effem.podcast.getCategories" + case .getRecentEpisodes: "/xrpc/xyz.effem.podcast.getRecentEpisodes" + case .getStats: "/xrpc/xyz.effem.podcast.getStats" } } @@ -135,17 +139,25 @@ extension EffemAPI: EndpointType { // MARK: Podcast Index Proxy - case .searchPodcasts(let query, let max): - return .requestParameters(encoding: .urlEncoding(parameters: ["q": query, "max": max])) + case .searchPodcasts(let query, let max, let searchType): + var params: Parameters = ["q": query, "max": max] + if let searchType { params["searchType"] = searchType } + return .requestParameters(encoding: .urlEncoding(parameters: params)) case .searchEpisodes(let query, let max): return .requestParameters(encoding: .urlEncoding(parameters: ["q": query, "max": max])) - case .getPodcast(let feedId): - return .requestParameters(encoding: .urlEncoding(parameters: ["feedId": feedId])) + case .getPodcast(let feedId, let itunesId): + var params: Parameters = [:] + if let feedId { params["feedId"] = feedId } + if let itunesId { params["itunesId"] = itunesId } + return .requestParameters(encoding: .urlEncoding(parameters: params)) - case .getEpisodes(let feedId, let max): - return .requestParameters(encoding: .urlEncoding(parameters: ["feedId": feedId, "max": max])) + case .getEpisodes(let feedId, let feedUrl, let max): + var params: Parameters = ["max": max] + if let feedId { params["feedId"] = feedId } + if let feedUrl { params["feedUrl"] = feedUrl } + return .requestParameters(encoding: .urlEncoding(parameters: params)) case .getEpisode(let episodeId): return .requestParameters(encoding: .urlEncoding(parameters: ["episodeId": episodeId])) @@ -158,6 +170,12 @@ extension EffemAPI: EndpointType { case .getCategories: return .request + + case .getRecentEpisodes(let max): + return .requestParameters(encoding: .urlEncoding(parameters: ["max": max])) + + case .getStats: + return .request } } diff --git a/Sources/EffemKit/EffemService.swift b/Sources/EffemKit/EffemService.swift index a636799..34dc0a8 100644 --- a/Sources/EffemKit/EffemService.swift +++ b/Sources/EffemKit/EffemService.swift @@ -140,8 +140,12 @@ public struct EffemService: Sendable { // MARK: - Podcast Index Proxy /// Searches for podcasts via the AppView's Podcast Index proxy. - public func searchPodcasts(query: String, max: Int = 20) async throws -> PodcastSearchResponse { - try await execute(.searchPodcasts(query: query, max: max)) + public func searchPodcasts( + query: String, + max: Int = 20, + searchType: String? = nil + ) async throws -> PodcastSearchResponse { + try await execute(.searchPodcasts(query: query, max: max, searchType: searchType)) } /// Searches for episodes via the AppView's Podcast Index proxy. @@ -149,14 +153,24 @@ public struct EffemService: Sendable { try await execute(.searchEpisodes(query: query, max: max)) } - /// Returns podcast metadata enriched with social overlay. + /// Returns podcast metadata by feed ID, enriched with social overlay. public func getPodcast(feedId: Int) async throws -> PodcastDetailResponse { - try await execute(.getPodcast(feedId: feedId)) + try await execute(.getPodcast(feedId: feedId, itunesId: nil)) + } + + /// Returns podcast metadata by iTunes ID, enriched with social overlay. + public func getPodcast(itunesId: Int) async throws -> PodcastDetailResponse { + try await execute(.getPodcast(feedId: nil, itunesId: itunesId)) } - /// Returns episodes for a podcast. + /// Returns episodes for a podcast by feed ID. public func getEpisodes(feedId: Int, max: Int = 20) async throws -> EpisodesResponse { - try await execute(.getEpisodes(feedId: feedId, max: max)) + try await execute(.getEpisodes(feedId: feedId, feedUrl: nil, max: max)) + } + + /// Returns episodes for a podcast by feed URL. + public func getEpisodes(feedUrl: String, max: Int = 20) async throws -> EpisodesResponse { + try await execute(.getEpisodes(feedId: nil, feedUrl: feedUrl, max: max)) } /// Returns a single episode's metadata. @@ -177,4 +191,14 @@ public struct EffemService: Sendable { public func getCategories() async throws -> CategoriesResponse { try await execute(.getCategories) } + + /// Returns recently published episodes across all podcasts. + public func getRecentEpisodes(max: Int = 20) async throws -> RecentEpisodesResponse { + try await execute(.getRecentEpisodes(max: max)) + } + + /// Returns Podcast Index network statistics. + public func getStats() async throws -> StatsResponse { + try await execute(.getStats) + } } diff --git a/Sources/EffemKit/Models/PodcastSearchResult.swift b/Sources/EffemKit/Models/PodcastSearchResult.swift index f242c91..10005a5 100644 --- a/Sources/EffemKit/Models/PodcastSearchResult.swift +++ b/Sources/EffemKit/Models/PodcastSearchResult.swift @@ -84,3 +84,26 @@ public struct CategoriesResponse: Codable, Sendable { public let feeds: [PodcastCategory] public let count: Int? } + +/// Response from `xyz.effem.podcast.getRecentEpisodes`. +public struct RecentEpisodesResponse: Codable, Sendable { + public let items: [EpisodeResult] + public let count: Int? + public let max: Int? +} + +/// Podcast Index network statistics. +public struct StatProperties: Codable, Sendable { + public let feedCountTotal: Int? + public let episodeCountTotal: Int? + public let feedsWithNewEpisodes3days: Int? + public let feedsWithNewEpisodes10days: Int? + public let feedsWithNewEpisodes30days: Int? + public let feedsWithNewEpisodes90days: Int? + public let feedsWithValueBlocks: Int? +} + +/// Response from `xyz.effem.podcast.getStats`. +public struct StatsResponse: Codable, Sendable { + public let stats: StatProperties +} -- 2.51.2