Skip to content

Commit

Permalink
Add support for reels media
Browse files Browse the repository at this point in the history
  • Loading branch information
ramtinak committed Aug 23, 2024
1 parent 2963c3f commit eef54eb
Show file tree
Hide file tree
Showing 12 changed files with 476 additions and 1 deletion.
6 changes: 5 additions & 1 deletion src/InstagramApiSharp/API/IInstaApi.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
* IRANIAN DEVELOPERS *
* *
* *
* 2023 *
* 2024 *
* *
* *
*/
Expand Down Expand Up @@ -115,6 +115,10 @@ public interface IInstaApi
/// </summary>
IShoppingProcessor ShoppingProcessor { get; }
/// <summary>
/// Reels api functions
/// </summary>
IReelProcessor ReelProcessor { get; }
/// <summary>
/// Instagram Web api functions.
/// <para>It's related to https://instagram.com/accounts/ </para>
/// </summary>
Expand Down
6 changes: 6 additions & 0 deletions src/InstagramApiSharp/API/InstaApi.cs
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,11 @@ public bool IsUserAuthenticated
/// User api functions.
/// </summary>
public IUserProcessor UserProcessor => _userProcessor;

/// <summary>
/// Reels api functions
/// </summary>
public IReelProcessor ReelProcessor { get; private set; }
/// <summary>
/// Helper processor for other processors
/// </summary>
Expand Down Expand Up @@ -3107,6 +3112,7 @@ private void InvalidateProcessors()
_businessProcessor = new BusinessProcessor(_deviceInfo, _user, _httpRequestProcessor, _logger, _userAuthValidate, this, _httpHelper);
_shoppingProcessor = new ShoppingProcessor(_deviceInfo, _user, _httpRequestProcessor, _logger, _userAuthValidate, this, _httpHelper);
_webProcessor = new WebProcessor(_deviceInfo, _user, _httpRequestProcessor, _logger, _userAuthValidate, this, _httpHelper);
ReelProcessor = new ReelProcessor(_deviceInfo, _user, _httpRequestProcessor, _logger, _userAuthValidate, this, _httpHelper);

}

Expand Down
9 changes: 9 additions & 0 deletions src/InstagramApiSharp/API/InstaApiConstants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,15 @@ internal static class InstaApiConstants

#endregion Main

#region Clips (reels) endpoints constants

public const string DISCOVER_VIDEOS_FEED = API_SUFFIX + "/discover/videos_feed/";
public const string CLIPS_WRITE_SEEN_STATE = API_SUFFIX + "/clips/write_seen_state/";
public const string CLIPS_USER = API_SUFFIX + "/clips/user/";
public const string DIRECT_BROADCAST_CLIPS_SHARE = API_SUFFIX + "/direct_v2/threads/broadcast/clip_share/";
public const string CLIPS_USER_SHARE_TO_FB_CONFIG = API_SUFFIX + "/clips/user/share_to_fb_config/";

#endregion Clips (reels) constants
#region Account endpoints constants

public const string ACCOUNTS_2FA_LOGIN = API_SUFFIX + "/accounts/two_factor_login/";
Expand Down
53 changes: 53 additions & 0 deletions src/InstagramApiSharp/API/Processors/IReelProcessor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*
* Developer: Ramtin Jokar [ [email protected] ] [ [email protected] ]
*
* IRANIAN DEVELOPERS
*/

using InstagramApiSharp.Classes;
using InstagramApiSharp.Classes.Models;
using System;
using System.Threading;
using System.Threading.Tasks;

namespace InstagramApiSharp.API.Processors
{
public interface IReelProcessor
{
/// <summary>
/// Get user's reels clips (medias)
/// </summary>
/// <param name="userId">User id (pk)</param>
/// <param name="paginationParameters">Pagination parameters: next id and max amount of pages to load</param>
Task<IResult<InstaReelsMediaList>> GetUserReelsClipsAsync(long userId, PaginationParameters paginationParameters);

/// <summary>
/// Get user's reels clips (medias)
/// </summary>
/// <param name="userId">User id (pk)</param>
/// <param name="paginationParameters">Pagination parameters: next id and max amount of pages to load</param>
/// <param name="cancellationToken">Cancellation token</param>
Task<IResult<InstaReelsMediaList>> GetUserReelsClipsAsync(long userId, PaginationParameters paginationParameters,
CancellationToken cancellationToken);

/// <summary>
/// Mark reel feed as seen
/// </summary>
/// <param name="mediaPkImpression">Media pk (from <see cref="InstaMedia.Pk"/> )</param>
Task<IResult<bool>> MarkReelAsSeenAsync(string mediaPkImpression);

/// <summary>
/// Explore reel feeds
/// </summary>
/// <param name="paginationParameters">Pagination parameters: next id and max amount of pages to load</param>
Task<IResult<InstaReelsMediaList>> GetReelsClipsAsync(PaginationParameters paginationParameters);

/// <summary>
/// Explore reel feeds
/// </summary>
/// <param name="paginationParameters">Pagination parameters: next id and max amount of pages to load</param>
/// <param name="cancellationToken">Cancellation token</param>
Task<IResult<InstaReelsMediaList>> GetReelsClipsAsync(PaginationParameters paginationParameters,
CancellationToken cancellationToken);
}
}
251 changes: 251 additions & 0 deletions src/InstagramApiSharp/API/Processors/ReelProcessor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,251 @@
/*
* Developer: Ramtin Jokar [ [email protected] ] [ [email protected] ]
*
* IRANIAN DEVELOPERS
*/

using InstagramApiSharp.Classes;
using InstagramApiSharp.Classes.Android.DeviceInfo;
using InstagramApiSharp.Classes.Models;
using InstagramApiSharp.Classes.ResponseWrappers;
using InstagramApiSharp.Converters;
using InstagramApiSharp.Converters.Json;
using InstagramApiSharp.Enums;
using InstagramApiSharp.Helpers;
using InstagramApiSharp.Logger;

using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;

namespace InstagramApiSharp.API.Processors
{
internal class ReelProcessor : IReelProcessor
{

#region Fields and constructor
private readonly AndroidDevice _deviceInfo;
private readonly IHttpRequestProcessor _httpRequestProcessor;
private readonly IInstaLogger _logger;
private readonly UserSessionData _user;
private readonly UserAuthValidate _userAuthValidate;
private readonly InstaApi _instaApi;
private readonly HttpHelper _httpHelper;

public ReelProcessor(AndroidDevice deviceInfo, UserSessionData user,
IHttpRequestProcessor httpRequestProcessor, IInstaLogger logger,
UserAuthValidate userAuthValidate, InstaApi instaApi, HttpHelper httpHelper)
{
_deviceInfo = deviceInfo;
_user = user;
_httpRequestProcessor = httpRequestProcessor;
_logger = logger;
_userAuthValidate = userAuthValidate;
_instaApi = instaApi;
_httpHelper = httpHelper;
}
#endregion Properties and constructor


/// <summary>
/// Get user's reels clips (medias)
/// </summary>
/// <param name="userId">User id (pk)</param>
/// <param name="paginationParameters">Pagination parameters: next id and max amount of pages to load</param>
public async Task<IResult<InstaReelsMediaList>> GetUserReelsClipsAsync(long userId,
PaginationParameters paginationParameters) =>
await GetReelsClips(paginationParameters, userId).ConfigureAwait(false);

/// <summary>
/// Get user's reels clips (medias)
/// </summary>
/// <param name="userId">User id (pk)</param>
/// <param name="paginationParameters">Pagination parameters: next id and max amount of pages to load</param>
/// <param name="cancellationToken">Cancellation token</param>
public async Task<IResult<InstaReelsMediaList>> GetUserReelsClipsAsync(long userId,
PaginationParameters paginationParameters, CancellationToken cancellationToken) =>
await GetReelsClips(paginationParameters, userId, cancellationToken).ConfigureAwait(false);

/// <summary>
/// Mark reel feed as seen
/// </summary>
/// <param name="mediaPkImpression">Media pk (from <see cref="InstaMedia.Pk"/> )</param>
public async Task<IResult<bool>> MarkReelAsSeenAsync(string mediaPkImpression)
{
UserAuthValidator.Validate(_userAuthValidate);
try
{
var instaUri = UriCreator.GetMarkReelAsSeenUri();
var impression = new JObject();

if (!string.IsNullOrEmpty(mediaPkImpression))
impression.Add(mediaPkImpression);

var data = new JObject
{
{"impressions", impression.ToString(Formatting.None)},
{"_uid", _user.LoggedInUser.Pk.ToString()},
{"_uuid", _deviceInfo.DeviceGuid.ToString()},
};

var request = _httpHelper.GetSignedRequest(HttpMethod.Post, instaUri, _deviceInfo, data);
var response = await _httpRequestProcessor.SendAsync(request);
var json = await response.Content.ReadAsStringAsync();

if (response.StatusCode != HttpStatusCode.OK)
return Result.UnExpectedResponse<bool>(response, json);

var obj = JsonConvert.DeserializeObject<InstaDefaultResponse>(json);
return obj.IsSucceed ? Result.Success(true) : Result.Fail<bool>(obj.Message);
}
catch (HttpRequestException httpException)
{
_logger?.LogException(httpException);
return Result.Fail(httpException, default(bool), ResponseType.NetworkProblem);
}
catch (Exception exception)
{
_logger?.LogException(exception);
return Result.Fail<bool>(exception);
}
}

/// <summary>
/// Explore reel feeds
/// </summary>
/// <param name="paginationParameters">Pagination parameters: next id and max amount of pages to load</param>
public async Task<IResult<InstaReelsMediaList>> GetReelsClipsAsync(PaginationParameters paginationParameters) =>
await GetReelsClips(paginationParameters).ConfigureAwait(false);

/// <summary>
/// Explore reel feeds
/// </summary>
/// <param name="paginationParameters">Pagination parameters: next id and max amount of pages to load</param>
/// <param name="cancellationToken">Cancellation token</param>
public async Task<IResult<InstaReelsMediaList>> GetReelsClipsAsync(PaginationParameters paginationParameters,
CancellationToken cancellationToken) =>
await GetReelsClips(paginationParameters, null, cancellationToken).ConfigureAwait(false);


private async Task<IResult<InstaReelsMediaList>> GetReelsClips(PaginationParameters paginationParameters,
long? userId = null, CancellationToken cancellationToken = default(CancellationToken))
{
UserAuthValidator.Validate(_userAuthValidate);
var reelFeeds = new InstaReelsMediaList();
try
{
if (paginationParameters == null)
paginationParameters = PaginationParameters.MaxPagesToLoad(1);

InstaReelsMediaList Convert(InstaReelsMediaListResponse instaReelFeedResponse)
{
return ConvertersFabric.Instance.GetReelsMediaListConverter(instaReelFeedResponse).Convert();
}
var timelineFeeds = await GetReels(paginationParameters, userId);
if (!timelineFeeds.Succeeded)
return Result.Fail(timelineFeeds.Info, reelFeeds);

var reelFeedResponse = timelineFeeds.Value;

reelFeeds = Convert(reelFeedResponse);
paginationParameters.NextMaxId = reelFeeds.NextMaxId;
paginationParameters.PagesLoaded++;

while (reelFeeds.MoreAvailable
&& !string.IsNullOrEmpty(paginationParameters.NextMaxId)
&& paginationParameters.PagesLoaded <= paginationParameters.MaximumPagesToLoad)
{
cancellationToken.ThrowIfCancellationRequested();

var nextFeed = await GetReels(paginationParameters, userId);
if (!nextFeed.Succeeded)
return Result.Fail(nextFeed.Info, reelFeeds);

var convertedFeed = Convert(nextFeed.Value);
reelFeeds.Medias.AddRange(convertedFeed.Medias);
reelFeeds.MoreAvailable = nextFeed.Value.PagingInfo?.MoreAvailable ?? false;
reelFeeds.NextMaxId = paginationParameters.NextMaxId = nextFeed.Value.PagingInfo?.MaxId;
paginationParameters.PagesLoaded++;
}

return Result.Success(reelFeeds);
}
catch (HttpRequestException httpException)
{
_logger?.LogException(httpException);
return Result.Fail(httpException, reelFeeds, ResponseType.NetworkProblem);
}
catch (Exception exception)
{
_logger?.LogException(exception);
return Result.Fail(exception, reelFeeds);
}
}

private async Task<IResult<InstaReelsMediaListResponse>> GetReels(PaginationParameters paginationParameters,
long? userId = null)
{
try
{
Uri instaUri;
Dictionary<string, string> data;
if (userId.HasValue)
{
instaUri = UriCreator.GetUserReelsClipsUri();
data = new Dictionary<string, string>
{
{"_uuid", _deviceInfo.DeviceGuid.ToString()},
{"target_user_id", userId.ToString()},
};
}
else
{
instaUri = UriCreator.GetReelsClipsUri();

var sessionInfo = new JObject
{
{"session_id", Guid.NewGuid().ToString()},
{"media_info", new JObject()},
};
data = new Dictionary<string, string>
{
{"seen_reels", "0"},
{"pct_reels", "0"},
{"tab_type", "clips_tab"},
{"session_info" ,sessionInfo.ToString(Formatting.None)},
{"_uuid", _deviceInfo.DeviceGuid.ToString()},
};
}
if (!string.IsNullOrEmpty(paginationParameters.NextMaxId))
data.Add("max_id", paginationParameters.NextMaxId);

var request = _httpHelper.GetDefaultRequest(HttpMethod.Post, instaUri, _deviceInfo, data);
var response = await _httpRequestProcessor.SendAsync(request);
var json = await response.Content.ReadAsStringAsync();

if (response.StatusCode != HttpStatusCode.OK)
return Result.UnExpectedResponse<InstaReelsMediaListResponse>(response, json);

return Result.Success(JsonConvert.DeserializeObject<InstaReelsMediaListResponse>(json));
}
catch (HttpRequestException httpException)
{
_logger?.LogException(httpException);
return Result.Fail(httpException, default(InstaReelsMediaListResponse), ResponseType.NetworkProblem);
}
catch (Exception exception)
{
_logger?.LogException(exception);
return Result.Fail(exception, default(InstaReelsMediaListResponse));
}
}

}
}
17 changes: 17 additions & 0 deletions src/InstagramApiSharp/Classes/Models/Reels/InstaReelsMediaList.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/*
* Developer: Ramtin Jokar [ [email protected] ] [ [email protected] ]
*
* IRANIAN DEVELOPERS
*/

using System.Collections.Generic;

namespace InstagramApiSharp.Classes.Models
{
public class InstaReelsMediaList : IInstaBaseList
{
public List<InstaMedia> Medias { get; set; } = new List<InstaMedia>();
public bool MoreAvailable { get; set; }
public string NextMaxId { get; set; }
}
}
Loading

0 comments on commit eef54eb

Please sign in to comment.