Skip to content

adding missing endpoints for tv shows #226

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 17 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion Sources/TMDb/Domain/APIClient/APIRequestQueryItem.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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")

}
214 changes: 214 additions & 0 deletions Sources/TMDb/Domain/Models/FindResponse.swift
Original file line number Diff line number Diff line change
@@ -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
}
}
18 changes: 18 additions & 0 deletions Sources/TMDb/Domain/Models/FindServiceType.swift
Original file line number Diff line number Diff line change
@@ -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"
}
31 changes: 31 additions & 0 deletions Sources/TMDb/Domain/Models/TVSeasonBasic.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
public struct TVSeasonBasic: Identifiable, Codable, Equatable, Hashable, Sendable {
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"
case episodeCount = "episode_count"
case id, name, overview
case posterPath = "poster_path"
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)
}

}
6 changes: 3 additions & 3 deletions Sources/TMDb/Domain/Models/TVSeries.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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
Expand Down
32 changes: 32 additions & 0 deletions Sources/TMDb/Domain/Services/Find/FindService.swift
Original file line number Diff line number Diff line change
@@ -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
}
37 changes: 37 additions & 0 deletions Sources/TMDb/Domain/Services/Find/Request/FindRequest.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
//
// MovieSearchRequest.swift
// TMDb
//
// Created by MLabs on 23/06/2025.
//

import Foundation

final class FindRequest: DecodableAPIRequest<FindResponse> {

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
}
}

}
Loading