Skip to content

Commit

Permalink
FEATURE: TV series external IDs (#134)
Browse files Browse the repository at this point in the history
* FEATURE: TV series external ids

* Update documentation
  • Loading branch information
adamayoung authored Jan 5, 2024
1 parent 0c4fd7a commit 8974cbf
Show file tree
Hide file tree
Showing 15 changed files with 296 additions and 8 deletions.
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")
)
}

}
41 changes: 41 additions & 0 deletions Tests/TMDbTests/Models/TVSeriesExternalLinksCollectionTests.swift
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

0 comments on commit 8974cbf

Please sign in to comment.