diff --git a/OpenTween.Tests/SocialProtocol/Twitter/TwitterProfileImageUriTest.cs b/OpenTween.Tests/SocialProtocol/Twitter/TwitterProfileImageUriTest.cs new file mode 100644 index 000000000..cfe0c049f --- /dev/null +++ b/OpenTween.Tests/SocialProtocol/Twitter/TwitterProfileImageUriTest.cs @@ -0,0 +1,93 @@ +// OpenTween - Client of Twitter +// Copyright (c) 2024 kim_upsilon (@kim_upsilon) +// All rights reserved. +// +// This file is part of OpenTween. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program. If not, see , or write to +// the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor, +// Boston, MA 02110-1301, USA. + +using System; +using Xunit; + +namespace OpenTween.SocialProtocol.Twitter +{ + public class TwitterProfileImageUriTest + { + [Theory] + [InlineData(24, "mini")] + [InlineData(25, "normal")] + [InlineData(48, "normal")] + [InlineData(49, "bigger")] + [InlineData(73, "bigger")] + [InlineData(74, "original")] + public void SizeName_GetPreferredSize_Test(int sizePx, string expected) + { + var size = TwitterProfileImageUri.SizeName.GetPreferredSize(sizePx); + Assert.Equal(expected, size.Name); + } + + [Fact] + public void SizeName_GetLargerOrSameSize_Test() + { + var expected = new[] + { + TwitterProfileImageUri.SizeName.Normal, + TwitterProfileImageUri.SizeName.Bigger, + TwitterProfileImageUri.SizeName.Original, + }; + Assert.Equal(expected, TwitterProfileImageUri.SizeName.GetLargerOrSameSize(minSizePx: 48)); + } + + [Theory] + [InlineData("https://pbs.twimg.com/profile_images/00000/foo_normal.jpg", 48, "https://pbs.twimg.com/profile_images/00000/foo_normal.jpg")] + [InlineData("https://pbs.twimg.com/profile_images/00000/foo_normal.jpg", 73, "https://pbs.twimg.com/profile_images/00000/foo_bigger.jpg")] + [InlineData("https://pbs.twimg.com/profile_images/00000/foo_normal.jpg", 24, "https://pbs.twimg.com/profile_images/00000/foo_mini.jpg")] + [InlineData("https://pbs.twimg.com/profile_images/00000/foo_normal.jpg", 100, "https://pbs.twimg.com/profile_images/00000/foo.jpg")] + [InlineData("https://pbs.twimg.com/profile_images/00000/foo_normal_bar_normal.jpg", 100, "https://pbs.twimg.com/profile_images/00000/foo_normal_bar.jpg")] + public void GetImageUri_Test(string normalUrl, int sizePx, string expected) + { + var responsiveImageUri = new TwitterProfileImageUri(normalUrl); + Assert.Equal(expected, responsiveImageUri.GetImageUri(sizePx).AbsoluteUri); + } + + [Fact] + public void GetImageUriLargerOrSameSize_Test() + { + var responsiveImageUri = new TwitterProfileImageUri("https://pbs.twimg.com/profile_images/00000/foo_normal.jpg"); + var expected = new Uri[] + { + new("https://pbs.twimg.com/profile_images/00000/foo_normal.jpg"), + new("https://pbs.twimg.com/profile_images/00000/foo_bigger.jpg"), + new("https://pbs.twimg.com/profile_images/00000/foo.jpg"), + }; + Assert.Equal(expected, responsiveImageUri.GetImageUriLargerOrSameSize(minSizePx: 48)); + } + + [Fact] + public void GetOriginalImageUri_Test() + { + var responsiveImageUri = new TwitterProfileImageUri("https://pbs.twimg.com/profile_images/00000/foo_normal.jpg"); + Assert.Equal(new("https://pbs.twimg.com/profile_images/00000/foo.jpg"), responsiveImageUri.GetOriginalImageUri()); + } + + [Fact] + public void GetFileName_Test() + { + var responsiveImageUri = new TwitterProfileImageUri("https://pbs.twimg.com/profile_images/00000/foo_normal.jpg"); + Assert.Equal("foo.jpg", responsiveImageUri.GetFilename()); + } + } +} diff --git a/OpenTween/Models/IResponsiveImageUri.cs b/OpenTween/Models/IResponsiveImageUri.cs new file mode 100644 index 000000000..d7116da77 --- /dev/null +++ b/OpenTween/Models/IResponsiveImageUri.cs @@ -0,0 +1,38 @@ +// OpenTween - Client of Twitter +// Copyright (c) 2024 kim_upsilon (@kim_upsilon) +// All rights reserved. +// +// This file is part of OpenTween. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program. If not, see , or write to +// the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor, +// Boston, MA 02110-1301, USA. + +#nullable enable + +using System; + +namespace OpenTween.Models +{ + public interface IResponsiveImageUri + { + public Uri GetImageUri(int sizePx); + + public Uri[] GetImageUriLargerOrSameSize(int minSizePx); + + public Uri GetOriginalImageUri(); + + public string GetFilename(); + } +} diff --git a/OpenTween/SocialProtocol/Twitter/TwitterProfileImageUri.cs b/OpenTween/SocialProtocol/Twitter/TwitterProfileImageUri.cs new file mode 100644 index 000000000..8ec1035fe --- /dev/null +++ b/OpenTween/SocialProtocol/Twitter/TwitterProfileImageUri.cs @@ -0,0 +1,101 @@ +// OpenTween - Client of Twitter +// Copyright (c) 2024 kim_upsilon (@kim_upsilon) +// All rights reserved. +// +// This file is part of OpenTween. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program. If not, see , or write to +// the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor, +// Boston, MA 02110-1301, USA. + +#nullable enable + +using System; +using System.IO; +using System.Linq; +using OpenTween.Models; + +namespace OpenTween.SocialProtocol.Twitter +{ + public record TwitterProfileImageUri( + string NormalImageUrlStr + ) : IResponsiveImageUri + { + public record SizeName + { + public string Name { get; init; } + + private SizeName(string name) + => this.Name = name; + + public static readonly SizeName Mini = new("mini"); + + public static readonly SizeName Normal = new("normal"); + + public static readonly SizeName Bigger = new("bigger"); + + public static readonly SizeName Original = new("original"); + + private static readonly (SizeName Size, int MaxSizePx)[] SizeNames = new[] + { + (Mini, 24), + (Normal, 48), + (Bigger, 73), + (Original, int.MaxValue), + }; + + public static SizeName GetPreferredSize(int sizePx) + => SizeNames.Where(x => sizePx <= x.MaxSizePx).First().Size; + + public static SizeName[] GetLargerOrSameSize(int minSizePx) + => SizeNames.Where(x => minSizePx <= x.MaxSizePx).Select(x => x.Size).ToArray(); + } + + public Uri GetImageUri(int sizePx) + { + var sizeName = SizeName.GetPreferredSize(sizePx); + + return this.GetImageUri(sizeName); + } + + public Uri GetImageUri(SizeName size) + { + var normalUrlStr = this.NormalImageUrlStr; + + // see: https://developer.twitter.com/en/docs/twitter-api/v1/accounts-and-users/user-profile-images-and-banners + string imageUrlStr; + if (size == SizeName.Normal) + imageUrlStr = normalUrlStr; + else if (size == SizeName.Original) + imageUrlStr = normalUrlStr.Replace("_normal.", "."); + else + imageUrlStr = normalUrlStr.Replace("_normal.", $"_{size.Name}."); + + return new(imageUrlStr); + } + + public Uri[] GetImageUriLargerOrSameSize(int minSizePx) + { + var sizes = SizeName.GetLargerOrSameSize(minSizePx); + + return sizes.Select(x => this.GetImageUri(x)).ToArray(); + } + + public Uri GetOriginalImageUri() + => this.GetImageUri(SizeName.Original); + + public string GetFilename() + => Path.GetFileName(this.GetOriginalImageUri().AbsolutePath); + } +}