From 2b38c508b02cefdc3844ab38259dd3fd3960494e Mon Sep 17 00:00:00 2001 From: MasterBlaster Code Date: Sat, 3 May 2025 16:30:08 +0200 Subject: [PATCH 01/17] add top_rated --- .../TVSeries/Requests/TopRatedTVSeriesRequest.swift | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 Sources/TMDb/Domain/Services/TVSeries/Requests/TopRatedTVSeriesRequest.swift diff --git a/Sources/TMDb/Domain/Services/TVSeries/Requests/TopRatedTVSeriesRequest.swift b/Sources/TMDb/Domain/Services/TVSeries/Requests/TopRatedTVSeriesRequest.swift new file mode 100644 index 00000000..0d40c65c --- /dev/null +++ b/Sources/TMDb/Domain/Services/TVSeries/Requests/TopRatedTVSeriesRequest.swift @@ -0,0 +1,12 @@ +import Foundation + + +final class TopRatedTVSeriesRequest: DecodableAPIRequest { + + init(page: Int? = nil, language: String? = nil) { + let path = "/tv/top_rated" + let queryItems = APIRequestQueryItems(page: page, language: language) + + super.init(path: path, queryItems: queryItems) + } +} From 7e5ebb63b895b8eb4c079e985f37d98c64030286 Mon Sep 17 00:00:00 2001 From: MasterBlaster Code Date: Sat, 3 May 2025 16:31:22 +0200 Subject: [PATCH 02/17] Create OnTheAirTVSeriesRequest.swift --- .../TVSeries/Requests/OnTheAirTVSeriesRequest.swift | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 Sources/TMDb/Domain/Services/TVSeries/Requests/OnTheAirTVSeriesRequest.swift diff --git a/Sources/TMDb/Domain/Services/TVSeries/Requests/OnTheAirTVSeriesRequest.swift b/Sources/TMDb/Domain/Services/TVSeries/Requests/OnTheAirTVSeriesRequest.swift new file mode 100644 index 00000000..3851e388 --- /dev/null +++ b/Sources/TMDb/Domain/Services/TVSeries/Requests/OnTheAirTVSeriesRequest.swift @@ -0,0 +1,12 @@ +import Foundation + +final class OnTheAirTVSeriesRequest: DecodableAPIRequest { + + init(page: Int? = nil, language: String? = nil) { + let path = "/tv/on_the_air" + let queryItems = APIRequestQueryItems(page: page, language: language) + + super.init(path: path, queryItems: queryItems) + } + +} From 8fe510440e51d0847ab029c0b81d84e52890e1ed Mon Sep 17 00:00:00 2001 From: MasterBlaster Code Date: Sat, 3 May 2025 16:32:04 +0200 Subject: [PATCH 03/17] Create AiringTodayTVSeriesRequest.swift --- .../Requests/AiringTodayTVSeriesRequest.swift | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 Sources/TMDb/Domain/Services/TVSeries/Requests/AiringTodayTVSeriesRequest.swift diff --git a/Sources/TMDb/Domain/Services/TVSeries/Requests/AiringTodayTVSeriesRequest.swift b/Sources/TMDb/Domain/Services/TVSeries/Requests/AiringTodayTVSeriesRequest.swift new file mode 100644 index 00000000..2a09eec9 --- /dev/null +++ b/Sources/TMDb/Domain/Services/TVSeries/Requests/AiringTodayTVSeriesRequest.swift @@ -0,0 +1,12 @@ +import Foundation + +final class AiringTodayTVSeriesRequest: DecodableAPIRequest { + + init(page: Int? = nil, language: String? = nil) { + let path = "/tv/popular" + let queryItems = APIRequestQueryItems(page: page, language: language) + + super.init(path: path, queryItems: queryItems) + } + +} From 9f13ddab562249283c442447e19c2eecbd351d8f Mon Sep 17 00:00:00 2001 From: MasterBlaster Code Date: Sat, 3 May 2025 16:34:53 +0200 Subject: [PATCH 04/17] Update TMDbTVSeriesService.swift adding topRated, onTheAir and airingToday --- .../TVSeries/TMDbTVSeriesService.swift | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/Sources/TMDb/Domain/Services/TVSeries/TMDbTVSeriesService.swift b/Sources/TMDb/Domain/Services/TVSeries/TMDbTVSeriesService.swift index e3170d84..2ef9a73c 100644 --- a/Sources/TMDb/Domain/Services/TVSeries/TMDbTVSeriesService.swift +++ b/Sources/TMDb/Domain/Services/TVSeries/TMDbTVSeriesService.swift @@ -167,6 +167,45 @@ final class TMDbTVSeriesService: TVSeriesService { return tvSeriesList } + + func topRated(page: Int? = nil, language: String? = nil) async throws -> TVSeriesPageableList { + let request = TopRatedTVSeriesRequest(page: page, language: language) + + let tvSeriesList: TVSeriesPageableList + do { + tvSeriesList = try await apiClient.perform(request) + } catch let error { + throw TMDbError(error: error) + } + + return tvSeriesList + } + + func onTheAir(page: Int? = nil, language: String? = nil) async throws -> TVSeriesPageableList { + let request = OnTheAirTVSeriesRequest(page: page, language: language) + + let tvSeriesList: TVSeriesPageableList + do { + tvSeriesList = try await apiClient.perform(request) + } catch let error { + throw TMDbError(error: error) + } + + return tvSeriesList + } + + func airingToday(page: Int? = nil, language: String? = nil) async throws -> TVSeriesPageableList { + let request = AiringTodayTVSeriesRequest(page: page, language: language) + + let tvSeriesList: TVSeriesPageableList + do { + tvSeriesList = try await apiClient.perform(request) + } catch let error { + throw TMDbError(error: error) + } + + return tvSeriesList + } func watchProviders( forTVSeries tvSeriesID: TVSeries.ID, From 3cf9dcd08ecc14809e8e451672bf7e84aadedbc9 Mon Sep 17 00:00:00 2001 From: MasterBlaster Code Date: Sat, 3 May 2025 16:37:26 +0200 Subject: [PATCH 05/17] Update AiringTodayTVSeriesRequest.swift --- .../Requests/AiringTodayTVSeriesRequest.swift | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/Sources/TMDb/Domain/Services/TVSeries/Requests/AiringTodayTVSeriesRequest.swift b/Sources/TMDb/Domain/Services/TVSeries/Requests/AiringTodayTVSeriesRequest.swift index 2a09eec9..dec0f581 100644 --- a/Sources/TMDb/Domain/Services/TVSeries/Requests/AiringTodayTVSeriesRequest.swift +++ b/Sources/TMDb/Domain/Services/TVSeries/Requests/AiringTodayTVSeriesRequest.swift @@ -10,3 +10,19 @@ final class AiringTodayTVSeriesRequest: DecodableAPIRequest Date: Sat, 3 May 2025 16:37:37 +0200 Subject: [PATCH 06/17] Update OnTheAirTVSeriesRequest.swift --- .../Requests/OnTheAirTVSeriesRequest.swift | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/Sources/TMDb/Domain/Services/TVSeries/Requests/OnTheAirTVSeriesRequest.swift b/Sources/TMDb/Domain/Services/TVSeries/Requests/OnTheAirTVSeriesRequest.swift index 3851e388..ac52026c 100644 --- a/Sources/TMDb/Domain/Services/TVSeries/Requests/OnTheAirTVSeriesRequest.swift +++ b/Sources/TMDb/Domain/Services/TVSeries/Requests/OnTheAirTVSeriesRequest.swift @@ -10,3 +10,19 @@ final class OnTheAirTVSeriesRequest: DecodableAPIRequest { } } + +extension APIRequestQueryItems { + + fileprivate init(page: Int?, language: String? = nil) { + self.init() + + if let page { + self[.page] = page + } + + if let language { + self[.language] = language + } + } + +} From ab341967e583bfdfaa5d78a8d51345aca2a126cb Mon Sep 17 00:00:00 2001 From: MasterBlaster Code Date: Sat, 3 May 2025 16:37:56 +0200 Subject: [PATCH 07/17] Update TopRatedTVSeriesRequest.swift --- .../Requests/TopRatedTVSeriesRequest.swift | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/Sources/TMDb/Domain/Services/TVSeries/Requests/TopRatedTVSeriesRequest.swift b/Sources/TMDb/Domain/Services/TVSeries/Requests/TopRatedTVSeriesRequest.swift index 0d40c65c..06eabf3d 100644 --- a/Sources/TMDb/Domain/Services/TVSeries/Requests/TopRatedTVSeriesRequest.swift +++ b/Sources/TMDb/Domain/Services/TVSeries/Requests/TopRatedTVSeriesRequest.swift @@ -10,3 +10,19 @@ final class TopRatedTVSeriesRequest: DecodableAPIRequest { super.init(path: path, queryItems: queryItems) } } + +extension APIRequestQueryItems { + + fileprivate init(page: Int?, language: String? = nil) { + self.init() + + if let page { + self[.page] = page + } + + if let language { + self[.language] = language + } + } + +} From 57d8fa98d9fb32c5bf603ece812444d059300875 Mon Sep 17 00:00:00 2001 From: MasterBlaster Code Date: Sat, 3 May 2025 16:45:36 +0200 Subject: [PATCH 08/17] Update TVSeriesService.swift final adding topRated, onTheAir a airingToday --- .../Services/TVSeries/TVSeriesService.swift | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/Sources/TMDb/Domain/Services/TVSeries/TVSeriesService.swift b/Sources/TMDb/Domain/Services/TVSeries/TVSeriesService.swift index 86bcae36..31bc6ba9 100644 --- a/Sources/TMDb/Domain/Services/TVSeries/TVSeriesService.swift +++ b/Sources/TMDb/Domain/Services/TVSeries/TVSeriesService.swift @@ -198,6 +198,58 @@ public protocol TVSeriesService: Sendable { /// func popular(page: Int?, language: String?) async throws -> TVSeriesPageableList + /// + /// Returns a list current Top Rated TV series. + /// + /// [TMDb API - TV Series Lists: Top Rated](https://developer.themoviedb.org/reference/tv-series-top-rated-list) + /// + /// - Precondition: `page` can be between `1` and `1000`. + /// + /// - Parameters: + /// - page: The page of results to return. + /// - language: ISO 639-1 language code to display results in. Defaults to `en`. + /// + /// - Throws: TMDb error ``TMDbError``. + /// + /// - Returns: Current Top Rated TV series as a pageable list. + /// + func topRated(page: Int?, language: String?) async throws -> TVSeriesPageableList + + /// + /// Returns a list current On The Air TV series. + /// + /// [TMDb API - TV Series Lists: On The Air](https://developer.themoviedb.org/reference/tv-series-on-the-air-list) + /// + /// - Precondition: `page` can be between `1` and `1000`. + /// + /// - Parameters: + /// - page: The page of results to return. + /// - language: ISO 639-1 language code to display results in. Defaults to `en`. + /// + /// - Throws: TMDb error ``TMDbError``. + /// + /// - Returns: Current On The Air TV series as a pageable list. + /// + func onTheAir(page: Int?, language: String?) async throws -> TVSeriesPageableList + + /// + /// Returns a list current Airing Today TV series. + /// + /// [TMDb API - TV Series Lists: Airing Today](https://developer.themoviedb.org/reference/tv-series-airing-today-list) + /// + /// - Precondition: `page` can be between `1` and `1000`. + /// + /// - Parameters: + /// - page: The page of results to return. + /// - language: ISO 639-1 language code to display results in. Defaults to `en`. + /// + /// - Throws: TMDb error ``TMDbError``. + /// + /// - Returns: Current On Airing Today TV series as a pageable list. + /// + func airingToday(page: Int?, language: String?) async throws -> TVSeriesPageableList + + /// /// Returns watch providers for a TV series /// From 31b8ef012e8a0f0ece6796eeb43768674d9488b5 Mon Sep 17 00:00:00 2001 From: MasterBlaster Code Date: Thu, 22 May 2025 21:24:09 +0200 Subject: [PATCH 09/17] Create TVSeasonBasic.swift --- Sources/TMDb/Domain/Models/TVSeasonBasic.swift | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 Sources/TMDb/Domain/Models/TVSeasonBasic.swift diff --git a/Sources/TMDb/Domain/Models/TVSeasonBasic.swift b/Sources/TMDb/Domain/Models/TVSeasonBasic.swift new file mode 100644 index 00000000..041f3187 --- /dev/null +++ b/Sources/TMDb/Domain/Models/TVSeasonBasic.swift @@ -0,0 +1,17 @@ +struct TVSeasonBasic: Codable { + let id: Int + let airDate: String? + let episodeCount: Int? + let name, overview, posterPath: String? + let seasonNumber: Int? + let voteAverage: Double? + + enum CodingKeys: String, CodingKey { + case airDate = "air_date" + case episodeCount = "episode_count" + case id, name, overview + case posterPath = "poster_path" + case seasonNumber = "season_number" + case voteAverage = "vote_average" + } +} From be1ad71018697cfe00ce348987fe99ee880d5ddd Mon Sep 17 00:00:00 2001 From: MasterBlaster Code Date: Thu, 22 May 2025 21:24:38 +0200 Subject: [PATCH 10/17] Update TVSeries.swift TVSeasonBasic --- Sources/TMDb/Domain/Models/TVSeries.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/TMDb/Domain/Models/TVSeries.swift b/Sources/TMDb/Domain/Models/TVSeries.swift index b4810f89..44d1f61c 100644 --- a/Sources/TMDb/Domain/Models/TVSeries.swift +++ b/Sources/TMDb/Domain/Models/TVSeries.swift @@ -72,7 +72,7 @@ public struct TVSeries: Identifiable, Codable, Equatable, Hashable, Sendable { /// /// Seasons in the TV series. /// - public let seasons: [TVSeason]? + public let seasons: [TVSeasonBasic]? /// /// TV series genres. From 5203ab3fcafe13d1b13fd68856aac8b5cc12d261 Mon Sep 17 00:00:00 2001 From: MasterBlaster Code Date: Thu, 22 May 2025 21:25:49 +0200 Subject: [PATCH 11/17] Update TVSeasonBasic.swift public --- Sources/TMDb/Domain/Models/TVSeasonBasic.swift | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Sources/TMDb/Domain/Models/TVSeasonBasic.swift b/Sources/TMDb/Domain/Models/TVSeasonBasic.swift index 041f3187..c04b5787 100644 --- a/Sources/TMDb/Domain/Models/TVSeasonBasic.swift +++ b/Sources/TMDb/Domain/Models/TVSeasonBasic.swift @@ -1,10 +1,10 @@ -struct TVSeasonBasic: Codable { - let id: Int - let airDate: String? - let episodeCount: Int? - let name, overview, posterPath: String? - let seasonNumber: Int? - let voteAverage: Double? +public struct TVSeasonBasic: Codable { + public let id: Int + public let airDate: String? + public let episodeCount: Int? + public let name, overview, posterPath: String? + public let seasonNumber: Int? + public let voteAverage: Double? enum CodingKeys: String, CodingKey { case airDate = "air_date" From f432a2632cfac991e2b3e317144b956a5f30d870 Mon Sep 17 00:00:00 2001 From: MasterBlaster Code Date: Thu, 22 May 2025 21:30:16 +0200 Subject: [PATCH 12/17] Update TVSeasonBasic.swift --- Sources/TMDb/Domain/Models/TVSeasonBasic.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/TMDb/Domain/Models/TVSeasonBasic.swift b/Sources/TMDb/Domain/Models/TVSeasonBasic.swift index c04b5787..cb01ef3f 100644 --- a/Sources/TMDb/Domain/Models/TVSeasonBasic.swift +++ b/Sources/TMDb/Domain/Models/TVSeasonBasic.swift @@ -1,4 +1,4 @@ -public struct TVSeasonBasic: Codable { +public struct TVSeasonBasic: Identifiable, Codable, Equatable, Hashable, Sendable { public let id: Int public let airDate: String? public let episodeCount: Int? From 476090ce0414a3c54d4da18a7674a692a3cf437d Mon Sep 17 00:00:00 2001 From: MasterBlaster Code Date: Thu, 22 May 2025 21:33:13 +0200 Subject: [PATCH 13/17] Update TVSeries.swift TVSeasonBasic --- Sources/TMDb/Domain/Models/TVSeries.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Sources/TMDb/Domain/Models/TVSeries.swift b/Sources/TMDb/Domain/Models/TVSeries.swift index 44d1f61c..9f158f91 100644 --- a/Sources/TMDb/Domain/Models/TVSeries.swift +++ b/Sources/TMDb/Domain/Models/TVSeries.swift @@ -205,7 +205,7 @@ public struct TVSeries: Identifiable, Codable, Equatable, Hashable, Sendable { episodeRunTime: [Int]? = nil, numberOfSeasons: Int? = nil, numberOfEpisodes: Int? = nil, - seasons: [TVSeason]? = nil, + seasons: [TVSeasonBasic]? = nil, genres: [Genre]? = nil, firstAirDate: Date? = nil, originCountry: [String]? = nil, @@ -313,7 +313,7 @@ extension TVSeries { self.episodeRunTime = try container.decodeIfPresent([Int].self, forKey: .episodeRunTime) self.numberOfSeasons = try container.decodeIfPresent(Int.self, forKey: .numberOfSeasons) self.numberOfEpisodes = try container.decodeIfPresent(Int.self, forKey: .numberOfEpisodes) - self.seasons = try container.decodeIfPresent([TVSeason].self, forKey: .seasons) + self.seasons = try container.decodeIfPresent([TVSeasonBasic].self, forKey: .seasons) self.genres = try container.decodeIfPresent([Genre].self, forKey: .genres) // Need to deal with empty strings - date decoding will fail with an empty string From ef74454df0d23f66c175fcad6be91daf6d1de260 Mon Sep 17 00:00:00 2001 From: MasterBlaster Code Date: Thu, 22 May 2025 21:36:51 +0200 Subject: [PATCH 14/17] Update TVSeasonBasic.swift --- Sources/TMDb/Domain/Models/TVSeasonBasic.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/TMDb/Domain/Models/TVSeasonBasic.swift b/Sources/TMDb/Domain/Models/TVSeasonBasic.swift index cb01ef3f..e88a7148 100644 --- a/Sources/TMDb/Domain/Models/TVSeasonBasic.swift +++ b/Sources/TMDb/Domain/Models/TVSeasonBasic.swift @@ -3,7 +3,7 @@ public struct TVSeasonBasic: Identifiable, Codable, Equatable, Hashable, Sendabl public let airDate: String? public let episodeCount: Int? public let name, overview, posterPath: String? - public let seasonNumber: Int? + public let seasonNumber: Int public let voteAverage: Double? enum CodingKeys: String, CodingKey { From 215e411b1972209559858a2772f0008423f30821 Mon Sep 17 00:00:00 2001 From: MasterBlaster Code Date: Thu, 22 May 2025 21:55:59 +0200 Subject: [PATCH 15/17] Update TMDbAPIClient.swift debugPrint --- Sources/TMDb/Networking/TMDbAPIClient.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/Sources/TMDb/Networking/TMDbAPIClient.swift b/Sources/TMDb/Networking/TMDbAPIClient.swift index 66475bfd..3889a587 100644 --- a/Sources/TMDb/Networking/TMDbAPIClient.swift +++ b/Sources/TMDb/Networking/TMDbAPIClient.swift @@ -60,6 +60,7 @@ final class TMDbAPIClient: APIClient, Sendable { let response: Request.Response do { + debugPrint("Data \(String(describing: String(data: data, encoding: .utf8)))") response = try await serialiser.decode(Request.Response.self, from: data) } catch let error { throw TMDbAPIError.decode(error) From 2b1df24ba7bf80de021a4d23e26ca9fcd0f1b0db Mon Sep 17 00:00:00 2001 From: MasterBlaster Code Date: Thu, 22 May 2025 22:18:34 +0200 Subject: [PATCH 16/17] Update TVSeasonBasic.swift add decoder --- Sources/TMDb/Domain/Models/TVSeasonBasic.swift | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/Sources/TMDb/Domain/Models/TVSeasonBasic.swift b/Sources/TMDb/Domain/Models/TVSeasonBasic.swift index e88a7148..da1f0b2d 100644 --- a/Sources/TMDb/Domain/Models/TVSeasonBasic.swift +++ b/Sources/TMDb/Domain/Models/TVSeasonBasic.swift @@ -14,4 +14,18 @@ public struct TVSeasonBasic: Identifiable, Codable, Equatable, Hashable, Sendabl case seasonNumber = "season_number" case voteAverage = "vote_average" } + + public init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + + self.id = try container.decode(Int.self, forKey: .id) + self.seasonNumber = try container.decodeIfPresent(Int.self, forKey: .seasonNumber) ?? 0 + self.airDate = try container.decodeIfPresent(String.self, forKey: .airDate) + self.episodeCount = try container.decodeIfPresent(Int.self, forKey: .episodeCount) + self.name = try container.decodeIfPresent(String.self, forKey: .name) + self.overview = try container.decodeIfPresent(String.self, forKey: .overview) + self.posterPath = try container.decodeIfPresent(String.self, forKey: .posterPath) + self.voteAverage = try container.decodeIfPresent(Double.self, forKey: .voteAverage) + } + } From 1ddbab3d23efb73a51d666d722e24f4157f7de9e Mon Sep 17 00:00:00 2001 From: BSC Date: Mon, 23 Jun 2025 13:53:30 +0200 Subject: [PATCH 17/17] add find by find by external sources --- .../APIClient/APIRequestQueryItem.swift | 3 +- Sources/TMDb/Domain/Models/FindResponse.swift | 214 ++++++++++++++++++ .../TMDb/Domain/Models/FindServiceType.swift | 18 ++ .../Domain/Services/Find/FindService.swift | 32 +++ .../Services/Find/Request/FindRequest.swift | 37 +++ .../Services/Find/TMDbFindService.swift | 32 +++ Sources/TMDb/TMDBClient.swift | 12 +- .../Services/Find/TMDbFindServiceTest.swift | 35 +++ Tests/TMDbTests/TestUtils/Tags.swift | 1 + 9 files changed, 381 insertions(+), 3 deletions(-) create mode 100644 Sources/TMDb/Domain/Models/FindResponse.swift create mode 100644 Sources/TMDb/Domain/Models/FindServiceType.swift create mode 100644 Sources/TMDb/Domain/Services/Find/FindService.swift create mode 100644 Sources/TMDb/Domain/Services/Find/Request/FindRequest.swift create mode 100644 Sources/TMDb/Domain/Services/Find/TMDbFindService.swift create mode 100644 Tests/TMDbTests/Domain/Services/Find/TMDbFindServiceTest.swift diff --git a/Sources/TMDb/Domain/APIClient/APIRequestQueryItem.swift b/Sources/TMDb/Domain/APIClient/APIRequestQueryItem.swift index 90100b54..a6e1f551 100644 --- a/Sources/TMDb/Domain/APIClient/APIRequestQueryItem.swift +++ b/Sources/TMDb/Domain/APIClient/APIRequestQueryItem.swift @@ -60,5 +60,6 @@ extension APIRequestQueryItem.Name { static let language = APIRequestQueryItem.Name("language") static let region = APIRequestQueryItem.Name("region") static let apiKey = APIRequestQueryItem.Name("api_key") - + static let externalSource = APIRequestQueryItem.Name("external_source") + } diff --git a/Sources/TMDb/Domain/Models/FindResponse.swift b/Sources/TMDb/Domain/Models/FindResponse.swift new file mode 100644 index 00000000..3f339242 --- /dev/null +++ b/Sources/TMDb/Domain/Models/FindResponse.swift @@ -0,0 +1,214 @@ +// +// SearchResponse.swift +// TMDb +// +// Created by MLabs on 23/06/2025. +// + + +import Foundation + +// MARK: - Main Response Structure +public struct FindResponse: Codable { + let movieResults: [MovieResult] + let personResults: [PersonResult] + let tvResults: [TVResult] + let tvEpisodeResults: [TVEpisodeResult] + let tvSeasonResults: [TVSeasonResult] + + enum CodingKeys: String, CodingKey { + case movieResults = "movie_results" + case personResults = "person_results" + case tvResults = "tv_results" + case tvEpisodeResults = "tv_episode_results" + case tvSeasonResults = "tv_season_results" + } +} + +// MARK: - TV Episode Result +public struct TVEpisodeResult: Codable { + let id: Int + let name: String + let overview: String + let mediaType: String + let voteAverage: Double + let voteCount: Int + let airDate: String + let episodeNumber: Int + let episodeType: String + let productionCode: String + let runtime: Int? + let seasonNumber: Int + let showId: Int + let stillPath: String? + + enum CodingKeys: String, CodingKey { + case id, name, overview + case mediaType = "media_type" + case voteAverage = "vote_average" + case voteCount = "vote_count" + case airDate = "air_date" + case episodeNumber = "episode_number" + case episodeType = "episode_type" + case productionCode = "production_code" + case runtime + case seasonNumber = "season_number" + case showId = "show_id" + case stillPath = "still_path" + } +} + +// MARK: - Person Result +public struct PersonResult: Codable { + let id: Int + let name: String + let originalName: String + let mediaType: String + let adult: Bool + let popularity: Double + let gender: Int + let knownForDepartment: String + let profilePath: String? + let knownFor: [KnownForItem] + + enum CodingKeys: String, CodingKey { + case id, name + case originalName = "original_name" + case mediaType = "media_type" + case adult, popularity, gender + case knownForDepartment = "known_for_department" + case profilePath = "profile_path" + case knownFor = "known_for" + } +} + +// MARK: - Known For Item +public struct KnownForItem: Codable { + let backdropPath: String? + let id: Int + let mediaType: String + let adult: Bool + let originalLanguage: String + let genreIds: [Int] + let popularity: Double + let voteAverage: Double + let voteCount: Int + let overview: String + let posterPath: String? + + // Pre filmy + let title: String? + let originalTitle: String? + let releaseDate: String? + let video: Bool? + + // Pre seriály + let name: String? + let originalName: String? + let firstAirDate: String? + let originCountry: [String]? + + enum CodingKeys: String, CodingKey { + case backdropPath = "backdrop_path" + case id + case mediaType = "media_type" + case adult + case originalLanguage = "original_language" + case genreIds = "genre_ids" + case popularity + case voteAverage = "vote_average" + case voteCount = "vote_count" + case overview + case posterPath = "poster_path" + case title + case originalTitle = "original_title" + case releaseDate = "release_date" + case video + case name + case originalName = "original_name" + case firstAirDate = "first_air_date" + case originCountry = "origin_country" + } +} + +// MARK: - Movie Result +public struct MovieResult: Codable { + let backdropPath: String? + let id: Int + let title: String + let originalTitle: String + let overview: String + let posterPath: String? + let mediaType: String + let adult: Bool + let originalLanguage: String + let genreIds: [Int] + let popularity: Double + let releaseDate: String + let video: Bool + let voteAverage: Double + let voteCount: Int + + enum CodingKeys: String, CodingKey { + case backdropPath = "backdrop_path" + case id, title + case originalTitle = "original_title" + case overview + case posterPath = "poster_path" + case mediaType = "media_type" + case adult + case originalLanguage = "original_language" + case genreIds = "genre_ids" + case popularity + case releaseDate = "release_date" + case video + case voteAverage = "vote_average" + case voteCount = "vote_count" + } +} + +// MARK: - TV Result +public struct TVResult: Codable { + let backdropPath: String? + let id: Int + let name: String + let originalName: String + let overview: String + let posterPath: String? + let mediaType: String + let adult: Bool + let originalLanguage: String + let genreIds: [Int] + let popularity: Double + let firstAirDate: String + let voteAverage: Double + let voteCount: Int + let originCountry: [String] + + enum CodingKeys: String, CodingKey { + case backdropPath = "backdrop_path" + case id, name + case originalName = "original_name" + case overview + case posterPath = "poster_path" + case mediaType = "media_type" + case adult + case originalLanguage = "original_language" + case genreIds = "genre_ids" + case popularity + case firstAirDate = "first_air_date" + case voteAverage = "vote_average" + case voteCount = "vote_count" + case originCountry = "origin_country" + } +} + +// MARK: - TV Season Result +public struct TVSeasonResult: Codable { + let id: Int + /// TODO: + + enum CodingKeys: String, CodingKey { + case id + } +} diff --git a/Sources/TMDb/Domain/Models/FindServiceType.swift b/Sources/TMDb/Domain/Models/FindServiceType.swift new file mode 100644 index 00000000..68c7dbbb --- /dev/null +++ b/Sources/TMDb/Domain/Models/FindServiceType.swift @@ -0,0 +1,18 @@ +// +// FindServiceType.swift +// TMDb +// +// Created by MLabs on 23/06/2025. +// + + +public enum FindServiceType: String, CodingKey { + case imdbID = "imdb_id" + case facebookID = "facebook_id" + case instagramID = "instagram_id" + case twitterID = "twitter_id" + case theTVDB = "tvdb_id" + case tikTok = "tiktok_id" + case wikidata = "wikidata_id" + case youTube = "youtube_id" +} diff --git a/Sources/TMDb/Domain/Services/Find/FindService.swift b/Sources/TMDb/Domain/Services/Find/FindService.swift new file mode 100644 index 00000000..9e476b0c --- /dev/null +++ b/Sources/TMDb/Domain/Services/Find/FindService.swift @@ -0,0 +1,32 @@ +// +// SearchService.swift +// TMDb +// +// Copyright © 2024 MLabs. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an AS IS BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation + +/// +/// Provides an interface for searching content from TMDb with external IDs.. +/// +@available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *) +public protocol FindService: Sendable { + + /// https://api.themoviedb.org/3/find/tt11952708?external_source=imdb_id' + /// + + func findId(_ id: String, type: FindServiceType) async throws -> FindResponse +} diff --git a/Sources/TMDb/Domain/Services/Find/Request/FindRequest.swift b/Sources/TMDb/Domain/Services/Find/Request/FindRequest.swift new file mode 100644 index 00000000..70a63a2c --- /dev/null +++ b/Sources/TMDb/Domain/Services/Find/Request/FindRequest.swift @@ -0,0 +1,37 @@ +// +// MovieSearchRequest.swift +// TMDb +// +// Created by MLabs on 23/06/2025. +// + +import Foundation + +final class FindRequest: DecodableAPIRequest { + + init(id: String, + externalSource: FindServiceType, + language: String? = nil + ) { + let path = "/find/\(id)" + let queryItems = APIRequestQueryItems(source: externalSource.rawValue, + language: language) + + super.init(path: path, queryItems: queryItems) + } + +} + +extension APIRequestQueryItems { + + fileprivate init(source: String, language: String?) { + self.init() + + self[.externalSource] = source + + if let language { + self[.language] = language + } + } + +} diff --git a/Sources/TMDb/Domain/Services/Find/TMDbFindService.swift b/Sources/TMDb/Domain/Services/Find/TMDbFindService.swift new file mode 100644 index 00000000..0868e820 --- /dev/null +++ b/Sources/TMDb/Domain/Services/Find/TMDbFindService.swift @@ -0,0 +1,32 @@ +// +// TMDbFindService.swift +// TMDb +// +// Created by MLabs on 23/06/2025. +// + +import Foundation + +@available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *) +final class TMDbFindService: FindService { + + private let apiClient: any APIClient + + init(apiClient: some APIClient) { + self.apiClient = apiClient + } + + + func findId(_ sourceId: String, type: FindServiceType) async throws -> FindResponse { + let request = FindRequest(id: sourceId, externalSource: type) + + let findResponse: FindResponse + do { + findResponse = try await apiClient.perform(request) + } catch let error { + throw TMDbError(error: error) + } + + return findResponse + } +} diff --git a/Sources/TMDb/TMDBClient.swift b/Sources/TMDb/TMDBClient.swift index 678afeba..cc43ca3c 100644 --- a/Sources/TMDb/TMDBClient.swift +++ b/Sources/TMDb/TMDBClient.swift @@ -73,6 +73,11 @@ public final class TMDbClient: Sendable { /// TMDb search. /// public let search: any SearchService + + /// + /// TMDb find. + /// + public let find: any FindService /// /// TMDb trending. @@ -141,7 +146,8 @@ public final class TMDbClient: Sendable { tvEpisodeService: TMDbTVEpisodeService(apiClient: apiClient), tvSeaonService: TMDbTVSeasonService(apiClient: apiClient), tvSeriesService: TMDbTVSeriesService(apiClient: apiClient), - watchProviderService: TMDbWatchProviderService(apiClient: apiClient) + watchProviderService: TMDbWatchProviderService(apiClient: apiClient), + find: TMDbFindService(apiClient: apiClient) ) } @@ -160,7 +166,8 @@ public final class TMDbClient: Sendable { tvEpisodeService: some TVEpisodeService, tvSeaonService: some TVSeasonService, tvSeriesService: some TVSeriesService, - watchProviderService: some WatchProviderService + watchProviderService: some WatchProviderService, + find: some FindService ) { self.account = accountService self.authentication = authenticationService @@ -177,6 +184,7 @@ public final class TMDbClient: Sendable { self.tvSeasons = tvSeaonService self.tvSeries = tvSeriesService self.watchProviders = watchProviderService + self.find = find } } diff --git a/Tests/TMDbTests/Domain/Services/Find/TMDbFindServiceTest.swift b/Tests/TMDbTests/Domain/Services/Find/TMDbFindServiceTest.swift new file mode 100644 index 00000000..954ef454 --- /dev/null +++ b/Tests/TMDbTests/Domain/Services/Find/TMDbFindServiceTest.swift @@ -0,0 +1,35 @@ +// +// TMDbFindServiceTest.swift +// TMDb +// +// Created by MLabs on 23/06/2025. +// + +import Foundation +import Testing + +@testable import TMDb + +@Suite(.tags(.services, .find)) +struct TMDbFindServiceTest { + + var service: TMDbFindService! + var apiClient: MockAPIClient! + + init() { + self.apiClient = MockAPIClient() + self.service = TMDbFindService(apiClient: apiClient) + } + + @Test("find by external id") + func movieGenresWithDefaultParameterValuesReturnsGenres() async throws { + + let expectedRequest = FindRequest(id: "tt11952708", + externalSource: .imdbID) + + let result = try await (service as FindService).findId("tt11952708", + type: .imdbID) + + print(result) + } +} diff --git a/Tests/TMDbTests/TestUtils/Tags.swift b/Tests/TMDbTests/TestUtils/Tags.swift index 127661d8..3598118c 100644 --- a/Tests/TMDbTests/TestUtils/Tags.swift +++ b/Tests/TMDbTests/TestUtils/Tags.swift @@ -43,6 +43,7 @@ extension Tag { @Tag static var movie: Self @Tag static var people: Self @Tag static var search: Self + @Tag static var find: Self @Tag static var trending: Self @Tag static var tvEpisode: Self @Tag static var tvSeason: Self