Skip to content
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

FEATURE: TV series external IDs #134

Merged
merged 2 commits into from
Jan 5, 2024
Merged
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
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ Documentation and examples of usage can be found at
## References

* [https://www.themoviedb.org](https://www.themoviedb.org)
* [https://developers.themoviedb.org](https://developers.themoviedb.org)
* [https://developer.themoviedb.org](https://developer.themoviedb.org)

## License

Expand Down
4 changes: 2 additions & 2 deletions Sources/TMDb/Models/IMDbLink.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ public struct IMDbLink: ExternalLink {
///
/// Creates an IMDb link object using an IMDb title identifier.
///
/// e.g. for a movie or TV show.
/// e.g. for a movie or TV series.
///
/// - Parameter imdbTitleID: The IMDb movie or TV show identifier.
/// - Parameter imdbTitleID: The IMDb movie or TV series identifier.
///
public init?(imdbTitleID: String?) {
guard
Expand Down
2 changes: 1 addition & 1 deletion Sources/TMDb/Models/PersonExternalLinksCollection.swift
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ extension PersonExternalLinksCollection {
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)

let id = try container.decode(Movie.ID.self, forKey: .id)
let id = try container.decode(Person.ID.self, forKey: .id)

let imdbID = try container.decodeIfPresent(String.self, forKey: .imdbID)
let wikiDataID = try container.decodeIfPresent(String.self, forKey: .wikiDataID)
Expand Down
134 changes: 134 additions & 0 deletions Sources/TMDb/Models/TVSeriesExternalLinksCollection.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
import Foundation

///
/// A model representing a collection of media databases and social IDs and links for a TV series.
///
public struct TVSeriesExternalLinksCollection: Identifiable, Codable, Equatable, Hashable {

///
/// The TMDb TV series identifier.
///
public let id: TVSeries.ID

///
/// IMDb link.
///
public let imdb: IMDbLink?

///
/// WikiData link.
///
public let wikiData: WikiDataLink?

///
/// Facebook link.
///
public let facebook: FacebookLink?

///
/// Instagram link.
///
public let instagram: InstagramLink?

///
/// Twitter link.
///
public let twitter: TwitterLink?

///
/// Creates an external links collection for a movie.
///
/// - Parameters:
/// - id: The TMDb TV series identifier.
/// - imdb: IMDb link.
/// - wikiData: WikiData link.
/// - facebook: Facebook link.
/// - instagram: Instagram link.
/// - twitter: Twitter link.
///
public init(
id: TVSeries.ID,
imdb: IMDbLink? = nil,
wikiData: WikiDataLink? = nil,
facebook: FacebookLink? = nil,
instagram: InstagramLink? = nil,
twitter: TwitterLink? = nil
) {
self.id = id
self.imdb = imdb
self.wikiData = wikiData
self.facebook = facebook
self.instagram = instagram
self.twitter = twitter
}

public func hash(into hasher: inout Hasher) {
hasher.combine(id)
hasher.combine(imdb?.id)
hasher.combine(wikiData?.id)
hasher.combine(facebook?.id)
hasher.combine(instagram?.id)
hasher.combine(twitter?.id)
}

public static func == (lhs: TVSeriesExternalLinksCollection, rhs: TVSeriesExternalLinksCollection) -> Bool {
lhs.id == rhs.id
&& lhs.imdb == rhs.imdb
&& lhs.wikiData == rhs.wikiData
&& lhs.facebook == rhs.facebook
&& lhs.instagram == rhs.instagram
&& lhs.twitter == rhs.twitter
}

}

extension TVSeriesExternalLinksCollection {

private enum CodingKeys: String, CodingKey {
case id
case imdbID = "imdbId"
case wikiDataID = "wikidataId"
case facebookID = "facebookId"
case instagramID = "instagramId"
case twitterID = "twitterId"
}

public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)

let id = try container.decode(TVSeries.ID.self, forKey: .id)

let imdbID = try container.decodeIfPresent(String.self, forKey: .imdbID)
let wikiDataID = try container.decodeIfPresent(String.self, forKey: .wikiDataID)
let facebookID = try container.decodeIfPresent(String.self, forKey: .facebookID)
let instagramID = try container.decodeIfPresent(String.self, forKey: .instagramID)
let twitterID = try container.decodeIfPresent(String.self, forKey: .twitterID)

let imdbLink = IMDbLink(imdbTitleID: imdbID)
let wikiDataLink = WikiDataLink(wikiDataID: wikiDataID)
let facebookLink = FacebookLink(facebookID: facebookID)
let instagramLink = InstagramLink(instagramID: instagramID)
let twitterLink = TwitterLink(twitterID: twitterID)

self.init(
id: id,
imdb: imdbLink,
wikiData: wikiDataLink,
facebook: facebookLink,
instagram: instagramLink,
twitter: twitterLink
)
}

public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)

try container.encode(id, forKey: .id)
try container.encodeIfPresent(imdb?.id, forKey: .imdbID)
try container.encodeIfPresent(wikiData?.id, forKey: .wikiDataID)
try container.encodeIfPresent(facebook?.id, forKey: .facebookID)
try container.encodeIfPresent(instagram?.id, forKey: .instagramID)
try container.encodeIfPresent(twitter?.id, forKey: .twitterID)
}

}
6 changes: 4 additions & 2 deletions Sources/TMDb/Movies/MovieService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -305,7 +305,7 @@ public final class MovieService {
///
/// Returns watch providers for a movie
///
/// [TMDb API - Movie: Watch providers](https://developers.themoviedb.org/3/movies/get-movie-watch-providers)
/// [TMDb API - Movie: Watch providers](https://developer.themoviedb.org/reference/movie-watch-providers)
/// - Parameters:
/// - id: The identifier of the movie.
///
Expand All @@ -329,7 +329,9 @@ public final class MovieService {

///
/// Returns a collection of media databases and social links for a movie.
///
///
/// [TMDb API - Movie: External IDs](https://developer.themoviedb.org/reference/movie-external-ids)
///
/// - Parameters:
/// - movieID: The identifier of the movie.
///
Expand Down
2 changes: 2 additions & 0 deletions Sources/TMDb/People/PersonService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,8 @@ public final class PersonService {
///
/// Returns a collection of media databases and social links for a person.
///
/// [TMDb API - People: External IDs](https://developer.themoviedb.org/reference/person-external-ids)
///
/// - Parameters:
/// - personID: The identifier of the person.
///
Expand Down
4 changes: 3 additions & 1 deletion Sources/TMDb/TMDb.docc/TMDb.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ For the TMDb API documentation, see
- ``Review``
- ``ImageCollection``
- ``VideoCollection``
- ``MovieExternalLinksCollection``
- ``MoviePageableList``

### TV Series
Expand All @@ -71,7 +72,7 @@ For the TMDb API documentation, see
- ``Review``
- ``ImageCollection``
- ``VideoCollection``
- ``TVSeriesPageableList``
- ``TVSeriesExternalLinksCollection``

### TV Seasons

Expand All @@ -96,6 +97,7 @@ For the TMDb API documentation, see
- ``PersonTVSeriesCredits``
- ``PersonImageCollection``
- ``Show``
- ``PersonExternalLinksCollection``
- ``PersonPageableList``

### Discover
Expand Down
6 changes: 6 additions & 0 deletions Sources/TMDb/TVSeries/Endpoints/TVSeriesEndpoint.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ enum TVSeriesEndpoint {
case similar(tvSeriesID: TVSeries.ID, page: Int? = nil)
case popular(page: Int? = nil)
case watch(tvSeriesID: TVSeries.ID)
case externalIDs(tvSeriesID: TVSeries.ID)

}

Expand Down Expand Up @@ -68,6 +69,11 @@ extension TVSeriesEndpoint: Endpoint {
return Self.basePath
.appendingPathComponent(tvSeriesID)
.appendingPathComponent("watch/providers")

case .externalIDs(let tvSeriesID):
return Self.basePath
.appendingPathComponent(tvSeriesID)
.appendingPathComponent("external_ids")
}
}

Expand Down
24 changes: 23 additions & 1 deletion Sources/TMDb/TVSeries/TVSeriesService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,8 @@ public final class TVSeriesService {
///
/// Returns watch providers for a TV series
///
/// [TMDb API - TVSeries: Watch providers](https://developers.themoviedb.org/3/tv/get-tv-watch-providers)
/// [TMDb API - TVSeries: Watch providers](https://developer.themoviedb.org/reference/tv-series-watch-providers)
///
/// - Parameters:
/// - id: The identifier of the TV series.
///
Expand All @@ -257,4 +258,25 @@ public final class TVSeriesService {
return result.results[regionCode]
}

///
/// Returns a collection of media databases and social links for a TV series.
///
/// [TMDb API - TVSeries: External IDs](https://developer.themoviedb.org/reference/tv-series-external-ids)
///
/// - Parameters:
/// - tvSeriesID: The identifier of the TV series.
///
/// - Returns: A collection of external links for the specificed TV series.
///
public func externalLinks(forTVSeries tvSeriesID: TVSeries.ID) async throws -> TVSeriesExternalLinksCollection {
let linksCollection: TVSeriesExternalLinksCollection
do {
linksCollection = try await apiClient.get(endpoint: TVSeriesEndpoint.externalIDs(tvSeriesID: tvSeriesID))
} catch let error {
throw TMDbError(error: error)
}

return linksCollection
}

}
13 changes: 13 additions & 0 deletions Tests/TMDbIntegrationTests/TVSeriesServiceTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -84,4 +84,17 @@ final class TVSeriesServiceTests: XCTestCase {
XCTAssertFalse(tvSeriesList.results.isEmpty)
}

func testExternalLinks() async throws {
let tvSeriesID = 86423

let linksCollection = try await tvSeriesService.externalLinks(forTVSeries: tvSeriesID)

XCTAssertEqual(linksCollection.id, tvSeriesID)
XCTAssertNotNil(linksCollection.imdb)
XCTAssertNil(linksCollection.wikiData)
XCTAssertNotNil(linksCollection.facebook)
XCTAssertNotNil(linksCollection.instagram)
XCTAssertNotNil(linksCollection.twitter)
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import Foundation
@testable import TMDb

extension TVSeriesExternalLinksCollection {

static func mock(
id: TVSeries.ID,
imdb: IMDbLink? = nil,
wikiData: WikiDataLink? = nil,
facebook: FacebookLink? = nil,
instagram: InstagramLink? = nil,
twitter: TwitterLink? = nil
) -> TVSeriesExternalLinksCollection {
TVSeriesExternalLinksCollection(
id: id,
imdb: imdb,
wikiData: wikiData,
facebook: facebook,
instagram: instagram,
twitter: twitter
)
}

static var lockeAndKey: TVSeriesExternalLinksCollection {
.mock(
id: 86423,
imdb: IMDbLink(imdbTitleID: "tt3007572"),
wikiData: nil,
facebook: FacebookLink(facebookID: "lockeandkeynetflix"),
instagram: InstagramLink(instagramID: "lockeandkeynetflix"),
twitter: TwitterLink(twitterID: "lockekeynetflix")
)
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
@testable import TMDb
import XCTest

final class TVSeriesExternalLinksCollectionTests: XCTestCase {

func testDecodeReturnsMovieExternalLinksCollection() throws {
let expectedResult = TVSeriesExternalLinksCollection(
id: 86423,
imdb: IMDbLink(imdbTitleID: "tt3007572"),
wikiData: nil,
facebook: FacebookLink(facebookID: "lockeandkeynetflix"),
instagram: InstagramLink(instagramID: "lockeandkeynetflix"),
twitter: TwitterLink(twitterID: "lockekeynetflix")
)

let result = try JSONDecoder.theMovieDatabase.decode(
TVSeriesExternalLinksCollection.self,
fromResource: "tv-series-external-ids"
)

XCTAssertEqual(result, expectedResult)
}

func testEncodeAndDecodeReturnsMovieExternalLinksCollection() throws {
let linksCollection = TVSeriesExternalLinksCollection(
id: 86423,
imdb: IMDbLink(imdbTitleID: "tt3007572"),
wikiData: nil,
facebook: FacebookLink(facebookID: "lockeandkeynetflix"),
instagram: InstagramLink(instagramID: "lockeandkeynetflix"),
twitter: TwitterLink(twitterID: "lockekeynetflix")
)

let data = try JSONEncoder.theMovieDatabase.encode(linksCollection)

let result = try JSONDecoder.theMovieDatabase.decode(TVSeriesExternalLinksCollection.self, from: data)

XCTAssertEqual(result, linksCollection)
}

}
12 changes: 12 additions & 0 deletions Tests/TMDbTests/Resources/json/tv-series-external-ids.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"id": 86423,
"imdb_id": "tt3007572",
"freebase_mid": null,
"freebase_id": null,
"tvdb_id": 361594,
"tvrage_id": null,
"wikidata_id": null,
"facebook_id": "lockeandkeynetflix",
"instagram_id": "lockeandkeynetflix",
"twitter_id": "lockekeynetflix"
}
Original file line number Diff line number Diff line change
Expand Up @@ -111,4 +111,12 @@ final class TVSeriesEndpointTests: XCTestCase {
XCTAssertEqual(url, expectedURL)
}

func testMovieExternalIDsEndpointReturnsURL() throws {
let expectedURL = try XCTUnwrap(URL(string: "/tv/1/external_ids"))

let url = TVSeriesEndpoint.externalIDs(tvSeriesID: 1).path

XCTAssertEqual(url, expectedURL)
}

}
Loading