-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(plex): add support for fetching friends' watchlists
- Loading branch information
Showing
16 changed files
with
333 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,131 @@ | ||
using Fetcharr.Cache.Core; | ||
using Fetcharr.Models.Configuration; | ||
using Fetcharr.Provider.Plex.Models; | ||
using Fetcharr.Shared.GraphQL; | ||
|
||
using GraphQL; | ||
using GraphQL.Client.Http; | ||
using GraphQL.Client.Serializer.SystemTextJson; | ||
|
||
using Microsoft.Extensions.DependencyInjection; | ||
using Microsoft.Extensions.Options; | ||
|
||
namespace Fetcharr.Provider.Plex.Clients | ||
{ | ||
/// <summary> | ||
/// Client for interacting with Plex' GraphQL API. | ||
/// </summary> | ||
public class PlexGraphQLClient( | ||
IOptions<FetcharrConfiguration> configuration, | ||
[FromKeyedServices("plex-graphql")] ICachingProvider cachingProvider) | ||
{ | ||
/// <summary> | ||
/// Gets the GraphQL endpoint for Plex. | ||
/// </summary> | ||
public const string GraphQLEndpoint = "https://community.plex.tv/api"; | ||
|
||
private readonly GraphQLHttpClient _client = | ||
new GraphQLHttpClient(PlexGraphQLClient.GraphQLEndpoint, new SystemTextJsonSerializer()) | ||
.WithAutomaticPersistedQueries(_ => true) | ||
.WithHeader("X-Plex-Token", configuration.Value.Plex.ApiToken) | ||
.WithHeader("X-Plex-Client-Identifier", "fetcharr"); | ||
|
||
/// <summary> | ||
/// Gets the watchlist of a Plex account, who's a friend of the current plex account. | ||
/// </summary> | ||
public async Task<IEnumerable<WatchlistMetadataItem>> GetFriendWatchlistAsync( | ||
string userId, | ||
int count = 100, | ||
string? cursor = null) | ||
{ | ||
string cacheKey = $"friend-watchlist-{userId}"; | ||
|
||
CacheValue<IEnumerable<WatchlistMetadataItem>> cachedResponse = await cachingProvider | ||
.GetAsync<IEnumerable<WatchlistMetadataItem>>(cacheKey); | ||
|
||
if(cachedResponse.HasValue) | ||
{ | ||
return cachedResponse.Value; | ||
} | ||
|
||
GraphQLRequest request = new() | ||
{ | ||
Query = """ | ||
query GetFriendWatchlist($uuid: ID = "", $first: PaginationInt!, $after: String) { | ||
user(id: $uuid) { | ||
watchlist(first: $first, after: $after) { | ||
nodes { | ||
... on MetadataItem { | ||
title | ||
ratingKey: id | ||
year | ||
type | ||
} | ||
} | ||
pageInfo { | ||
hasNextPage | ||
endCursor | ||
} | ||
} | ||
} | ||
} | ||
""", | ||
OperationName = "GetFriendWatchlist", | ||
Variables = new | ||
{ | ||
uuid = userId, | ||
first = count, | ||
after = cursor ?? string.Empty | ||
} | ||
}; | ||
|
||
GraphQLResponse<PlexUserWatchlistResponseType> response = await this._client | ||
.SendQueryAsync<PlexUserWatchlistResponseType>(request); | ||
|
||
response.ThrowIfErrors(message: "Failed to fetch friend's watchlist from Plex"); | ||
|
||
IEnumerable<WatchlistMetadataItem> watchlistItems = response.Data.User.Watchlist.Nodes; | ||
|
||
await cachingProvider.SetAsync(cacheKey, watchlistItems, expiration: TimeSpan.FromHours(4)); | ||
return watchlistItems; | ||
} | ||
|
||
/// <summary> | ||
/// Gets all the friends of the current Plex account and returns them. | ||
/// </summary> | ||
public async Task<IEnumerable<PlexFriendUser>> GetAllFriendsAsync() | ||
{ | ||
CacheValue<IEnumerable<PlexFriendUser>> cachedResponse = await cachingProvider | ||
.GetAsync<IEnumerable<PlexFriendUser>>("friends-list"); | ||
|
||
if(cachedResponse.HasValue) | ||
{ | ||
return cachedResponse.Value; | ||
} | ||
|
||
GraphQLRequest request = new() | ||
{ | ||
Query = """ | ||
query { | ||
allFriendsV2 { | ||
user { | ||
id | ||
username | ||
} | ||
} | ||
} | ||
""" | ||
}; | ||
|
||
GraphQLResponse<PlexFriendListResponseType> response = await this._client | ||
.SendQueryAsync<PlexFriendListResponseType>(request); | ||
|
||
response.ThrowIfErrors(message: "Failed to fetch friends list from Plex"); | ||
|
||
IEnumerable<PlexFriendUser> friends = response.Data.Friends.Select(v => v.User); | ||
|
||
await cachingProvider.SetAsync("friends-list", friends, expiration: TimeSpan.FromHours(4)); | ||
return friends; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
10 changes: 10 additions & 0 deletions
10
src/Provider.Plex/src/Models/Friends/PlexFriendListResponseType.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
using System.Text.Json.Serialization; | ||
|
||
namespace Fetcharr.Provider.Plex.Models | ||
{ | ||
public class PlexFriendListResponseType | ||
{ | ||
[JsonPropertyName("allFriendsV2")] | ||
public List<PlexFriendUserContainer> Friends { get; set; } = []; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
using System.Text.Json.Serialization; | ||
|
||
namespace Fetcharr.Provider.Plex.Models | ||
{ | ||
/// <summary> | ||
/// Representation of a friend user account. | ||
/// </summary> | ||
public class PlexFriendUser | ||
{ | ||
[JsonPropertyName("id")] | ||
public string Id { get; set; } = string.Empty; | ||
|
||
[JsonPropertyName("username")] | ||
public string Username { get; set; } = string.Empty; | ||
} | ||
} |
13 changes: 13 additions & 0 deletions
13
src/Provider.Plex/src/Models/Friends/PlexFriendUserContainer.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
using System.Text.Json.Serialization; | ||
|
||
namespace Fetcharr.Provider.Plex.Models | ||
{ | ||
/// <summary> | ||
/// Representation of a friend user account container. | ||
/// </summary> | ||
public class PlexFriendUserContainer | ||
{ | ||
[JsonPropertyName("user")] | ||
public PlexFriendUser User { get; set; } = new(); | ||
} | ||
} |
16 changes: 16 additions & 0 deletions
16
src/Provider.Plex/src/Models/Friends/PlexUserWatchlistResponseType.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
using System.Text.Json.Serialization; | ||
|
||
namespace Fetcharr.Provider.Plex.Models | ||
{ | ||
public class PlexUserWatchlistResponseType | ||
{ | ||
[JsonPropertyName("user")] | ||
public PlexWatchlistResponseType User { get; set; } = new(); | ||
} | ||
|
||
public class PlexWatchlistResponseType | ||
{ | ||
[JsonPropertyName("watchlist")] | ||
public PaginatedResult<WatchlistMetadataItem> Watchlist { get; set; } = new(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
using System.Text.Json.Serialization; | ||
|
||
namespace Fetcharr.Provider.Plex.Models | ||
{ | ||
public class PaginatedResult<T> | ||
{ | ||
[JsonPropertyName("nodes")] | ||
public List<T> Nodes { get; set; } = []; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
using Fetcharr.Provider.Plex.Clients; | ||
using Fetcharr.Provider.Plex.Models; | ||
|
||
namespace Fetcharr.Provider.Plex | ||
{ | ||
/// <summary> | ||
/// Client for fetching friends' watchlists from Plex. | ||
/// </summary> | ||
public class PlexFriendsWatchlistClient( | ||
PlexGraphQLClient plexGraphQLClient) | ||
{ | ||
/// <summary> | ||
/// Fetch the watchlists for all the friends the current Plex account and return them. | ||
/// </summary> | ||
/// <param name="count">Maximum amount of items to fetch per watchlist.</param> | ||
public async Task<IEnumerable<WatchlistMetadataItem>> FetchAllWatchlistsAsync(int count = 10) | ||
{ | ||
List<WatchlistMetadataItem> joinedWatchlist = []; | ||
IEnumerable<PlexFriendUser> friends = await plexGraphQLClient.GetAllFriendsAsync(); | ||
|
||
foreach(PlexFriendUser friend in friends) | ||
{ | ||
IEnumerable<WatchlistMetadataItem> friendWatchlist = await plexGraphQLClient | ||
.GetFriendWatchlistAsync(friend.Id, count); | ||
|
||
joinedWatchlist.AddRange(friendWatchlist); | ||
} | ||
|
||
return joinedWatchlist; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.