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

V4 #47

Merged
merged 20 commits into from
Jul 18, 2023
Merged

V4 #47

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
60 changes: 48 additions & 12 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
@@ -1,35 +1,71 @@
name: .NET

on:
push:
branches: [ master ]
push:
branches:
- main
pull_request:
branches: [ master ]

workflow_dispatch:
inputs:
Environment:
description: 'Environment'
type: environment
required: true

jobs:
build:
runs-on: ubuntu-latest
environment: ${{ github.event.inputs.Environment }}
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Setup .NET
uses: actions/setup-dotnet@v1
uses: actions/setup-dotnet@v3
with:
dotnet-version: 3.1.x
dotnet-version: 6.0.x
- name: Restore dependencies
run: dotnet restore
- name: Build
run: dotnet build --no-restore
- name: Test
run: dotnet test --no-build --verbosity normal
run: dotnet test --no-build --verbosity normal --logger GitHubActions
- name: Pack #If we're not in production, add a version suffix to the package. This indicates pre-release
if: github.environment != 'Production'
run: dotnet pack ./src/TinyPNG --configuration release --output ${{ github.workspace}}/artifact/ /p:VersionSuffix=${{ github.run_number}}
- name: Pack
run: dotnet pack --configuration release --output ${{ github.workspace}}/artifact/
- uses: actions/upload-artifact@v2
if: github.environment == 'Production'
run: dotnet pack ./src/TinyPNG --configuration release --output ${{ github.workspace}}/artifact/

- name: Upload artifact
uses: actions/upload-artifact@v3
with:
name: tinypng
path: ${{ github.workspace}}/artifact/
if-no-files-found: error
- name: Push
run: dotnet nuget push ${{ github.workspace}}/artifact/*.nupkg --source https://nuget.pkg.github.com/ctolkien/index.json --api-key ${GITHUB_TOKEN} --skip-duplicate
- name: Push to GitHub NuGet Package Registry
run: dotnet nuget push ${{ github.workspace}}/artifact/*.nupkg --source https://nuget.pkg.github.com/ctolkien/index.json --api-key ${{secrets.GITHUB_TOKEN}} --skip-duplicate

- name: Push to NuGet Package Registry
if: github.environment == 'Production'
run: dotnet nuget push ${{ github.workspace}}/artifact/*.nupkg --api-key ${{ secrets.NUGET_APIKEY }} --skip-duplicate

- name: Extract version
shell: pwsh
working-directory: src/TinyPNG
run: |
$version = ([xml](Get-Content TinyPNG.csproj)).Project.PropertyGroup.VersionPrefix[0].Trim()
Add-Content -Path $env:GITHUB_ENV -Value "VERSION=$version"
Add-Content -Path $env:GITHUB_ENV -Value "VERSIONWITHSUFFIX=$version-${{ github.run_number }}"

- name: Create GitHub Release
if: github.environment != 'Production'
run: gh release create "${{ env.VERSIONWITHSUFFIX }}" --generate-notes --prerelease
env:
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
- name: Create GitHub Release
if: github.environment == 'Production'
run: gh release create "${{ env.VERSION }}" --generate-notes
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}


14 changes: 7 additions & 7 deletions Directory.Build.targets
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
<Project>
<PropertyGroup>
<TargetFrameworkMonikerAssemblyAttributesPath>$([System.IO.Path]::Combine('$(IntermediateOutputPath)','$(TargetFrameworkMoniker).AssemblyAttributes$(DefaultLanguageSourceExtension)'))</TargetFrameworkMonikerAssemblyAttributesPath>
</PropertyGroup>
<ItemGroup>
<EmbeddedFiles Include="$(GeneratedAssemblyInfoFile)"/>
</ItemGroup>
<Project>
<PropertyGroup>
<TargetFrameworkMonikerAssemblyAttributesPath>$([System.IO.Path]::Combine('$(IntermediateOutputPath)','$(TargetFrameworkMoniker).AssemblyAttributes$(DefaultLanguageSourceExtension)'))</TargetFrameworkMonikerAssemblyAttributesPath>
</PropertyGroup>
<ItemGroup>
<EmbeddedFiles Include="$(GeneratedAssemblyInfoFile)"/>
</ItemGroup>
</Project>
33 changes: 30 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,15 @@ var result = await png.Compress("cat.jpg");
result.Output.Url;
```

## Upgrading from V2
# Version Upgrades

## Upgrading from V3 to V4

* The namespaces have changed for the extension methods to all reside in the `TinyPng` namespace. This will avoid needing to bring in two different namespaces.
* The `CompressFromUrl` method has been removed. This is now available via a new overload for `Compress` which takes in a `Uri` object
* I've standardised the namespace on `TinyPng`, it was a bit of a mixed bag of casing previously.

## Upgrading from V2 to V3

The API has changed from V2, primarily you no longer need to await each individual
step of using the TinyPNG API, you can now chain appropriate calls together as
Expand Down Expand Up @@ -121,12 +129,31 @@ await compressTask.Resize(new CoverResizeOperation(width, height));

The same `Byte[]`, `Stream`, `File` and `Url` path API's are available from the result of the `Resize()` method.

## Converting Formats (v4)

You can convert images to different formats using the `Convert()` method. This will return a object which contains the converted image data.

```csharp
using var png = new TinyPngClient("yourSecretApiKey");

var compressAndConvert = await png.Compress("cat.png").Convert(ConvertImageFormat.Wildcard);
```

By using the `Wildcard` format, TinyPng will return the best type for the supplied image.

In the scenario that you are converting to an image and losing transparency, you can specify a background colour to use for the image.

```csharp
var compressAndConvert = await png.Compress("cat.png").Convert(ConvertImageFormat.Wildcard, "#FF0000");
```


## Amazon S3 Storage

The result of any compress operation can be stored directly on to Amazon S3 storage. I'd strongly recommend referring to [TinyPNG.com's documentation](https://tinypng.com/developers/reference) with regard to how to configure
the appropriate S3 access.

If you're going to be storing images for most requests onto S3, then you can pass in an `AmazonS3Configuration` object to the constructor.
If you're going to be storing images for most requests into S3, then you can pass in an `AmazonS3Configuration` object to the constructor which will be used for all subsequent requests.

```csharp
using var png = new TinyPngClient("yourSecretApiKey",
Expand Down Expand Up @@ -170,7 +197,7 @@ compressedCat.CompressionCount; // = 5

## HttpClient

TinyPngClient can take HttpClient, which can be controlled from outside the library.
TinyPngClient can take HttpClient as constructor overload, the lifetime of which can be controlled from outside the library.

```csharp
var httpClient = new HttpClient();
Expand Down
80 changes: 43 additions & 37 deletions TinyPNG.sln
Original file line number Diff line number Diff line change
@@ -1,37 +1,43 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.30002.166
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TinyPNG", "src\TinyPNG\TinyPNG.csproj", "{52901FDB-68CE-4192-8B2B-3BD1773261F3}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TinyPng.Tests", "tests\TinyPng.Tests\TinyPng.Tests.csproj", "{DBA34C0E-C2A8-4D42-8770-FADC6251E93D}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{466E1CAF-57A2-4370-91C7-455AAC8E4D2E}"
ProjectSection(SolutionItems) = preProject
.gitignore = .gitignore
azure-pipelines.yml = azure-pipelines.yml
README.md = README.md
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{52901FDB-68CE-4192-8B2B-3BD1773261F3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{52901FDB-68CE-4192-8B2B-3BD1773261F3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{52901FDB-68CE-4192-8B2B-3BD1773261F3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{52901FDB-68CE-4192-8B2B-3BD1773261F3}.Release|Any CPU.Build.0 = Release|Any CPU
{DBA34C0E-C2A8-4D42-8770-FADC6251E93D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{DBA34C0E-C2A8-4D42-8770-FADC6251E93D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DBA34C0E-C2A8-4D42-8770-FADC6251E93D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DBA34C0E-C2A8-4D42-8770-FADC6251E93D}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {54A4A777-ED82-4D34-A6E1-AACC3E946A40}
EndGlobalSection
EndGlobal
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.6.33712.159
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TinyPNG", "src\TinyPNG\TinyPNG.csproj", "{52901FDB-68CE-4192-8B2B-3BD1773261F3}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TinyPng.Tests", "tests\TinyPng.Tests\TinyPng.Tests.csproj", "{DBA34C0E-C2A8-4D42-8770-FADC6251E93D}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{466E1CAF-57A2-4370-91C7-455AAC8E4D2E}"
ProjectSection(SolutionItems) = preProject
.gitignore = .gitignore
.github\workflows\main.yml = .github\workflows\main.yml
README.md = README.md
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TinyPNG.Samples", "src\TinyPNG.Samples\TinyPNG.Samples.csproj", "{F4BE9305-126D-49CF-830F-E840A337EF13}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{52901FDB-68CE-4192-8B2B-3BD1773261F3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{52901FDB-68CE-4192-8B2B-3BD1773261F3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{52901FDB-68CE-4192-8B2B-3BD1773261F3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{52901FDB-68CE-4192-8B2B-3BD1773261F3}.Release|Any CPU.Build.0 = Release|Any CPU
{DBA34C0E-C2A8-4D42-8770-FADC6251E93D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{DBA34C0E-C2A8-4D42-8770-FADC6251E93D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DBA34C0E-C2A8-4D42-8770-FADC6251E93D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DBA34C0E-C2A8-4D42-8770-FADC6251E93D}.Release|Any CPU.Build.0 = Release|Any CPU
{F4BE9305-126D-49CF-830F-E840A337EF13}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F4BE9305-126D-49CF-830F-E840A337EF13}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F4BE9305-126D-49CF-830F-E840A337EF13}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F4BE9305-126D-49CF-830F-E840A337EF13}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {54A4A777-ED82-4D34-A6E1-AACC3E946A40}
EndGlobalSection
EndGlobal
17 changes: 17 additions & 0 deletions src/TinyPNG.Samples/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using TinyPng;


var tinyPngClient = new TinyPngClient("lolwat");

//var response = await tinyPngClient.Compress(@"./Resources/cat.jpg");
//var x = await tinyPngClient.Compress(@"./Resources/cat.jpg").Resize(100, 100);
//var y = await tinyPngClient.Compress(@"./Resources/cat.jpg").Download().GetImageByteData();

var q = await tinyPngClient.Compress(@"./Resources/cat.jpg").Convert(ConvertImageFormat.Wildcard);

//Console.WriteLine($"Compression Count {x.CompressionCount}");
//Console.WriteLine($"Byte length {y.Length}");

Console.WriteLine($"Converted type = {q.ContentType}");

Console.ReadKey();
Binary file added src/TinyPNG.Samples/Resources/cat.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/TinyPNG.Samples/Resources/compressedcat.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/TinyPNG.Samples/Resources/resizedcat.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
27 changes: 27 additions & 0 deletions src/TinyPNG.Samples/TinyPNG.Samples.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<PackRelease>false</PackRelease>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\TinyPNG\TinyPNG.csproj" />
</ItemGroup>

<ItemGroup>
<None Update="Resources\cat.jpg">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Resources\compressedcat.jpg">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Resources\resizedcat.jpg">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>

</Project>
14 changes: 7 additions & 7 deletions src/TinyPNG/AmazonS3Configuration.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
using Newtonsoft.Json;
using System.Text.Json.Serialization;

namespace TinyPng
{
public class AmazonS3Configuration
{
[JsonProperty("service")]
[JsonPropertyName("service")]
public const string Service = "s3";

public AmazonS3Configuration(string awsAccessKeyId,
Expand All @@ -18,17 +18,17 @@ public AmazonS3Configuration(string awsAccessKeyId,
Region = defaultRegion;
}

[JsonProperty("aws_access_key_id")]
public string AwsAccessKeyId { get; }
[JsonProperty("aws_secret_access_key")]
public string AwsSecretAccessKey { get; }
[JsonPropertyName("aws_access_key_id")]
public string AwsAccessKeyId { get; }
[JsonPropertyName("aws_secret_access_key")]
public string AwsSecretAccessKey { get; }
public string Region { get; set; }
[JsonIgnore]
public string Bucket { get; set; }
[JsonIgnore]
public string Path { get; set; }

[JsonProperty("path")]
[JsonPropertyName("path")]
public string BucketPath
{
get
Expand Down
67 changes: 67 additions & 0 deletions src/TinyPNG/CustomJsonStringEnumConverter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime.Serialization;
using System.Text.Json;
using System.Text.Json.Serialization;

namespace TinyPng
{
/// <summary>
/// Pinched from https://stackoverflow.com/questions/59059989/system-text-json-how-do-i-specify-a-custom-name-for-an-enum-value
/// This is because System.Text.Json doesn't support EnumMemberAttribute or a way to customise what an Enum value is serialised as.
/// </summary>
internal class CustomJsonStringEnumConverter : JsonConverterFactory
{
private readonly JsonNamingPolicy namingPolicy;
private readonly bool allowIntegerValues;
private readonly JsonStringEnumConverter baseConverter;

public CustomJsonStringEnumConverter() : this(null, true) { }

public CustomJsonStringEnumConverter(JsonNamingPolicy namingPolicy = null, bool allowIntegerValues = true)
{
this.namingPolicy = namingPolicy;
this.allowIntegerValues = allowIntegerValues;
this.baseConverter = new JsonStringEnumConverter(namingPolicy, allowIntegerValues);
}

public override bool CanConvert(Type typeToConvert) => baseConverter.CanConvert(typeToConvert);

public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options)
{
var query = from field in typeToConvert.GetFields(BindingFlags.Public | BindingFlags.Static)
let attr = field.GetCustomAttribute<EnumMemberAttribute>()
where attr != null
select (field.Name, attr.Value);
var dictionary = query.ToDictionary(p => p.Name, p => p.Value);
if (dictionary.Count > 0)
{
return new JsonStringEnumConverter(new DictionaryLookupNamingPolicy(dictionary, namingPolicy), allowIntegerValues).CreateConverter(typeToConvert, options);
}
else
{
return baseConverter.CreateConverter(typeToConvert, options);
}
}
}

public class JsonNamingPolicyDecorator : JsonNamingPolicy
{
readonly JsonNamingPolicy underlyingNamingPolicy;

public JsonNamingPolicyDecorator(JsonNamingPolicy underlyingNamingPolicy) => this.underlyingNamingPolicy = underlyingNamingPolicy;

public override string ConvertName(string name) => underlyingNamingPolicy == null ? name : underlyingNamingPolicy.ConvertName(name);
}

internal class DictionaryLookupNamingPolicy : JsonNamingPolicyDecorator
{
readonly Dictionary<string, string> dictionary;

public DictionaryLookupNamingPolicy(Dictionary<string, string> dictionary, JsonNamingPolicy underlyingNamingPolicy) : base(underlyingNamingPolicy) => this.dictionary = dictionary ?? throw new ArgumentNullException();

public override string ConvertName(string name) => dictionary.TryGetValue(name, out var value) ? value : base.ConvertName(name);
}
}
Loading