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

Add docs for all methods, modify DownloadPhoto to thrown an exception… #7

Merged
merged 1 commit into from
Mar 1, 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
59 changes: 53 additions & 6 deletions StudioClient/DownloadHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,12 @@ namespace SkylabStudio
public class PhotoOptions
{
public List<Image>? Bgs { get; set; }
public bool? ReturnOnError { get; set; }

// Default constructor (parameterless)
public PhotoOptions()
{
Bgs = new List<Image>();
ReturnOnError = false;
}
}

Expand All @@ -30,6 +31,11 @@ public DownloadAllPhotosResult()
}
public partial class StudioClient
{
/// <summary>
/// Downloads background images based on the provided profile.
/// </summary>
/// <param name="profile">The profile associated to the job.</param>
/// <returns>List of downloaded background images.</returns>
private async Task<List<Image>?> DownloadBgImages(dynamic profile)
{
List<Image> tempBgs = new List<Image>();
Expand All @@ -45,6 +51,11 @@ public partial class StudioClient
return tempBgs;
}

/// <summary>
/// Downloads an image asynchronously from the specified URL.
/// </summary>
/// <param name="imageUrl">The URL of the image to download.</param>
/// <returns>The downloaded image as a byte array.</returns>
private static async Task<byte[]?> DownloadImageAsync(string imageUrl)
{
if (!imageUrl.StartsWith("http", StringComparison.OrdinalIgnoreCase)) {
Expand All @@ -71,6 +82,16 @@ public partial class StudioClient
}
}

/// <summary>
/// Downloads and replaces the background image in the input image.
/// Required: Profile should have a background uploaded and replace background toggled on
/// </summary>
/// <param name="fileName">The name of the file being processed.</param>
/// <param name="inputImage">The input image to process.</param>
/// <param name="outputPath">The path where the processed images will be saved.</param>
/// <param name="profile">The profile associated to the job.</param>
/// <param name="bgs">List of background images.</param>
/// <returns>True if the operation is successful; otherwise, false.</returns>
private async Task<bool> DownloadReplacedBackgroundImage(string fileName, Image inputImage, string outputPath, dynamic? profile = null, List<Image>? bgs = null)
{
try
Expand Down Expand Up @@ -98,11 +119,20 @@ private async Task<bool> DownloadReplacedBackgroundImage(string fileName, Image
}
catch (Exception ex)
{
Console.Error.WriteLine($"Error downloading background image: {ex.Message}");
return false;
string errorMsg = $"Error downloading background image: {ex.Message}";
Console.Error.WriteLine(errorMsg);

throw new Exception(errorMsg);
}
}

/// <summary>
/// Downloads all photos based on a list of photo IDs.
/// </summary>
/// <param name="photosList">List of photo objects with IDs.</param>
/// <param name="profile">The profile associated to the job.</param>
/// <param name="outputPath">The path where photos will be downloaded.</param>
/// <returns>Download result containing lists of success and errored photo names.</returns>
public async Task<DownloadAllPhotosResult> DownloadAllPhotos(JArray photosList, dynamic profile, string outputPath)
{
if (!Directory.Exists(outputPath))
Expand All @@ -127,6 +157,7 @@ public async Task<DownloadAllPhotosResult> DownloadAllPhotos(JArray photosList,
List<Task<Tuple<string, bool>>> downloadTasks = new List<Task<Tuple<string, bool>>>();
PhotoOptions photoOptions = new PhotoOptions
{
ReturnOnError = true,
Bgs = bgs
};
foreach (string photoId in photoIds)
Expand Down Expand Up @@ -163,6 +194,20 @@ public async Task<DownloadAllPhotosResult> DownloadAllPhotos(JArray photosList,
return downloadResults;
}
}

/// <summary>
/// Downloads a photo based on the specified photo ID.
/// </summary>
/// <param name="photoId">The ID of the photo to download.</param>
/// <param name="outputPath">The path where the downloaded photo will be saved. Could either be </param>
/// <param name="profile">Optional: The profile containing photo processing options.</param>
/// <param name="options">Optional: Additional options for photo processing.</param>
/// <param name="semaphore">Optional - *Used Interally with DownloadAllPhotos* : SemaphoreSlim for controlling concurrent photo downloads.</param>
/// <returns>
/// A tuple containing the downloaded photo's filename and a boolean indicating
/// whether the download was successful.
/// </returns>
/// <exception cref="Exception">Thrown on any download error when DownloadPhoto is called without ReturnOnError option.</exception>
public async Task<Tuple<string,bool>> DownloadPhoto(long photoId, string outputPath, dynamic? profile = null, PhotoOptions? options = null, SemaphoreSlim? semaphore = null)
{
string fileName = "";
Expand Down Expand Up @@ -194,7 +239,7 @@ public async Task<Tuple<string,bool>> DownloadPhoto(long photoId, string output
// Load output image
byte[] imageBuffer = await DownloadImageAsync(photo.retouchedUrl.Value);
Image image = Image.NewFromBuffer(imageBuffer);

if (isExtract) { // Output extract image
string pngFileName = $"{Path.GetFileNameWithoutExtension(fileName)}.png";

Expand All @@ -218,8 +263,10 @@ public async Task<Tuple<string,bool>> DownloadPhoto(long photoId, string output
return new (fileName, true);
} catch (Exception _e)
{
Console.Error.WriteLine($"Failed to download photo id: {photoId}");
Console.Error.WriteLine(_e);
string errorMsg = $"Failed to download photo id: {photoId} - ${_e}";
if (options?.ReturnOnError == null) {
throw new Exception(errorMsg);
}

return new (fileName, false);
} finally
Expand Down
46 changes: 46 additions & 0 deletions StudioClient/Job.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,46 +3,92 @@ namespace SkylabStudio
{
public partial class StudioClient
{
/// <summary>
/// Retrieves a list of all jobs.
/// </summary>
/// <returns>A dynamic object representing the list of jobs.</returns>
public async Task<dynamic> ListJobs()
{
return await Request("jobs", RestSharp.Method.Get);
}

/// <summary>
/// Creates a new job with the specified payload.
/// </summary>
/// <param name="payload">The job payload to be sent in the request.</param>
/// <returns>A dynamic object representing the created job.</returns>
public async Task<dynamic> CreateJob(object payload)
{
return await Request("jobs", RestSharp.Method.Post, payload);
}

/// <summary>
/// Retrieves information about a specific job based on its ID.
/// </summary>
/// <param name="jobId">The ID of the job to retrieve.</param>
/// <returns>A dynamic object representing the requested job.</returns>
public async Task<dynamic> GetJob(long jobId)
{
return await Request($"jobs/{jobId}", RestSharp.Method.Get);
}

/// <summary>
/// Retrieves information about a job based on its name.
/// </summary>
/// <param name="jobName">The name of the job to retrieve.</param>
/// <returns>A dynamic object representing the requested job.</returns>
public async Task<dynamic> GetJobByName(string jobName)
{
return await Request($"jobs/find_by_name/?name={jobName}", RestSharp.Method.Get);
}

/// <summary>
/// Updates a specific job with the provided payload.
/// </summary>
/// <param name="jobId">The ID of the job to update.</param>
/// <param name="payload">The job payload to be sent in the request.</param>
/// <returns>A dynamic object representing the updated job.</returns>
public async Task<dynamic> UpdateJob(long jobId, object payload)
{
return await Request($"jobs/{jobId}", RestSharp.Method.Put, payload);
}

/// <summary>
/// Queues a specific job with the provided payload.
/// </summary>
/// <param name="jobId">The ID of the job to queue.</param>
/// <param name="payload">The job payload to be sent in the request.</param>
/// <returns>A dynamic object representing the queued job.</returns>
public async Task<dynamic> QueueJob(long jobId, object payload)
{
return await Request($"jobs/{jobId}/queue", RestSharp.Method.Post, payload);
}

/// <summary>
/// Cancels a specific job based on its ID.
/// </summary>
/// <param name="jobId">The ID of the job to cancel.</param>
/// <returns>A dynamic object representing the result of the cancellation.</returns>
public async Task<dynamic> CancelJob(long jobId)
{
return await Request($"jobs/{jobId}/cancel", RestSharp.Method.Post);
}

/// <summary>
/// Deletes a specific job based on its ID.
/// </summary>
/// <param name="jobId">The ID of the job to delete.</param>
/// <returns>A dynamic object representing the result of the deletion.</returns>
public async Task<dynamic> DeleteJob(long jobId)
{
return await Request($"jobs/{jobId}", RestSharp.Method.Delete);
}

/// <summary>
/// Retrieves information about jobs that are in front of the specified job.
/// </summary>
/// <param name="jobId">The ID of the reference job.</param>
/// <returns>A dynamic object representing jobs that are in front of the reference job.</returns>
public async Task<dynamic> JobsInFront(long jobId)
{
return await Request($"jobs/{jobId}/jobs_in_front", RestSharp.Method.Get);
Expand Down
54 changes: 51 additions & 3 deletions StudioClient/Photo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,41 +7,89 @@ namespace SkylabStudio
{
public partial class StudioClient
{
public static readonly string[] VALID_EXTENSIONS = { ".png", ".jpg", ".jpeg", "webp" };
/// <summary>
/// Array of valid file extensions for photos.
/// </summary>
public static readonly string[] VALID_EXTENSIONS = { ".png", ".jpg", ".jpeg", ".webp" };

/// <summary>
/// Maximum allowed size for a photo in bytes.
/// </summary>
public const int MAX_PHOTO_SIZE = 27 * 1024 * 1024;

/// <summary>
/// Creates a new photo with the specified payload.
/// </summary>
/// <param name="payload">The photo payload to be sent in the request.</param>
/// <returns>A dynamic object representing the created photo.</returns>
public async Task<dynamic> CreatePhoto(object payload)
{
return await Request("photos", Method.Post, payload);
}

/// <summary>
/// Retrieves information about a specific photo based on its ID.
/// </summary>
/// <param name="photoId">The ID of the photo to retrieve.</param>
/// <returns>A dynamic object representing the requested photo.</returns>
public async Task<dynamic> GetPhoto(long photoId)
{
return await Request($"photos/{photoId}", Method.Get);
}

/// <summary>
/// Deletes a specific photo based on its ID.
/// </summary>
/// <param name="photoId">The ID of the photo to delete.</param>
/// <returns>A dynamic object representing the result of the deletion.</returns>
public async Task<dynamic> DeletePhoto(long photoId)
{
return await Request($"photos/{photoId}", Method.Delete);
}

/// <summary>
/// Retrieves a presigned URL for uploading a photo.
/// </summary>
/// <param name="photoId">The ID of the photo for which to get the upload URL.</param>
/// <param name="md5">The MD5 hash of the photo data.</param>
/// <param name="useCacheUpload">Flag indicating whether to use cache upload.</param>
/// <returns>A dynamic object representing the presigned upload URL.</returns>
public async Task<dynamic> GetUploadUrl(long photoId, string md5 = "", bool useCacheUpload = false)
{
string queryParams = $"use_cache_upload={useCacheUpload.ToString().ToLower()}&photo_id={photoId}&content_md5={md5}";

return await Request($"photos/upload_url?{queryParams}", Method.Get);
}

/// <summary>
/// Uploads a photo associated with a job.
/// </summary>
/// <param name="photoPath">The path to the photo file.</param>
/// <param name="jobId">The ID of the job associated with the photo.</param>
/// <returns>A dynamic object representing the uploaded photo.</returns>
public async Task<dynamic> UploadJobPhoto(string photoPath, long jobId)
{
return await UploadPhoto(photoPath, "job", jobId);
}

/// <summary>
/// Uploads a photo associated with a profile.
/// </summary>
/// <param name="photoPath">The path to the photo file.</param>
/// <param name="profileId">The ID of the profile associated with the photo.</param>
/// <returns>A dynamic object representing the uploaded photo.</returns>
public async Task<dynamic> UploadProfilePhoto(string photoPath, long profileId)
{
return await UploadPhoto(photoPath, "profile", profileId);
}

/// <summary>
/// Uploads a photo associated with a job or profile.
/// </summary>
/// <param name="photoPath">The path to the photo file.</param>
/// <param name="modelName">The name of the model (job or profile).</param>
/// <param name="modelId">The ID of the model associated with the photo.</param>
/// <returns>A dynamic object representing the uploaded photo.</returns>
private async Task<dynamic> UploadPhoto(string photoPath, string modelName, long modelId)
{
string[] availableModels = { "job", "profile" };
Expand All @@ -51,7 +99,7 @@ private async Task<dynamic> UploadPhoto(string photoPath, string modelName, long
string fileExtension = Path.GetExtension(photoBasename).ToLower();
if (!VALID_EXTENSIONS.Contains(fileExtension))
{
throw new Exception("Photo has invalid extension. Supported extensions (.jpg, .jpeg, .png, .webp)");
throw new Exception("Photo has an invalid extension. Supported extensions (.jpg, .jpeg, .png, .webp)");
}

var photoObject = new JObject
Expand Down Expand Up @@ -82,7 +130,7 @@ private async Task<dynamic> UploadPhoto(string photoPath, string modelName, long

request.AddParameter("application/octet-stream", fileBytes, ParameterType.RequestBody);
request.AddHeader("Content-MD5", Convert.ToBase64String(MD5.Create().ComputeHash(fileBytes)));
if (modelName == "job") request.AddHeader("X-Amz-Tagging", "job=photo&api=true");
if (modelName == "job") request.AddHeader("X-Amz-Tagging", "job=photo&api=true");

// Upload image via PUT request to presigned url
RestResponse response = await httpClient.ExecuteAsync(request);
Expand Down
25 changes: 25 additions & 0 deletions StudioClient/Profile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,51 @@ namespace SkylabStudio
{
public partial class StudioClient
{
/// <summary>
/// Creates a new profile with the specified payload.
/// </summary>
/// <param name="payload">The payload containing profile information.</param>
/// <returns>A dynamic object representing the created profile.</returns>
public async Task<dynamic> CreateProfile(object payload)
{
return await Request("profiles", RestSharp.Method.Post, payload);
}

/// <summary>
/// Retrieves a list of profiles.
/// </summary>
/// <returns>A dynamic object representing a list of profiles.</returns>
public async Task<dynamic> ListProfiles()
{
return await Request("profiles", RestSharp.Method.Get);
}

/// <summary>
/// Retrieves the profile with the specified ID.
/// </summary>
/// <param name="profileId">The ID of the profile to retrieve.</param>
/// <returns>A dynamic object representing the retrieved profile.</returns>
public async Task<dynamic> GetProfile(long profileId)
{
return await Request($"profiles/{profileId}", RestSharp.Method.Get);
}

/// <summary>
/// Updates the profile with the specified ID using the provided payload.
/// </summary>
/// <param name="profileId">The ID of the profile to update.</param>
/// <param name="payload">The payload containing updated profile information.</param>
/// <returns>A dynamic object representing the updated profile.</returns>
public async Task<dynamic> UpdateProfile(long profileId, object payload)
{
return await Request($"profiles/{profileId}", RestSharp.Method.Put, payload);
}

/// <summary>
/// Retrieves background photos associated with the profile identified by the given profile ID.
/// </summary>
/// <param name="profileId">The ID of the profile to retrieve background photos for.</param>
/// <returns>A dynamic object representing background photos of the profile.</returns>
public async Task<dynamic> GetProfileBgs(long profileId)
{
return await Request($"profiles/{profileId}/bg_photos", RestSharp.Method.Get);
Expand Down
Loading
Loading