Skip to content

Commit 8d81258

Browse files
committed
* syntax changes for arrays
* switching to tagging for nuget releases * adding latest chapters to read-list-exports * fixing defect with rate limit service
1 parent 4fff4b3 commit 8d81258

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

52 files changed

+245
-149
lines changed

.github/workflows/main.yml

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ name: CI
66
# events but only for the main branch
77
on:
88
push:
9-
branches: [ release/* ]
9+
tags:
10+
- "v[0-9]+.[0-9]+.[0-9]+"
1011

1112
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
1213
jobs:
@@ -25,11 +26,17 @@ jobs:
2526
uses: actions/setup-dotnet@v3
2627
with:
2728
dotnet-version: '8.x'
29+
30+
- name: Get version
31+
id: version
32+
run: |
33+
echo "version-without-v=${GITHUB_REF#refs/tags/v}" >> $GITHUB_OUTPUT
2834
2935
- name: Publish Extensions Nuget Package
3036
id: publish_nuget_extensions
3137
uses: alirezanet/publish-nuget@v3.0.4
3238
with:
3339
PROJECT_FILE_PATH: src/MangaDexSharp/MangaDexSharp.csproj
34-
TAG_COMMIT: true
35-
NUGET_KEY: ${{secrets.NUGET_API_KEY}}
40+
TAG_COMMIT: false
41+
NUGET_KEY: ${{secrets.NUGET_API_KEY}}
42+
VERSION_STATIC: ${{ steps.version.outputs.version-without-v }}

src/MangaDexSharp.UpdatesPoll/Program.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,4 @@ await srv.Poll(chapters =>
2222
chapters.Length,
2323
string.Join("\r\n\t", chapters.Select(t => $"{t.Chapter.Attributes?.Chapter} - Vol {t.Chapter.Attributes?.Volume} - {t.Chapter.Attributes?.Title}: Pages: {t.PageUrls.Length}")));
2424
return Task.CompletedTask;
25-
}, langs: new[] { "en" });
25+
}, langs: ["en"]);

src/MangaDexSharp.Utilities.Cli/MangaDexSharp.Utilities.Cli.csproj

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,10 @@
1919
<ProjectReference Include="..\MangaDexSharp\MangaDexSharp.csproj" />
2020
</ItemGroup>
2121

22+
<ItemGroup>
23+
<None Update="appsettings.json">
24+
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
25+
</None>
26+
</ItemGroup>
27+
2228
</Project>

src/MangaDexSharp.Utilities.Cli/Services/ExportReadListService.cs

Lines changed: 66 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,28 @@
11
namespace MangaDexSharp.Utilities.Cli.Services;
22

3+
using System.Text;
34
using Writers;
4-
using MangaStatus = (Manga manga, ReadStatus status);
5+
using MangaStatus = (Manga manga, ReadStatus status, Chapter? chapter);
56

67
public interface IExportReadListService
78
{
8-
Task WriteUsersMangaStatusToFile(string file, ReadStatus? status, CancellationToken token);
9+
Task WriteUsersMangaStatusToFile(string file, ReadStatus? status, bool includeChapters, string lang, CancellationToken token);
910
}
1011

1112
internal class ExportReadListService(
1213
IRateLimitService _api,
1314
ILogger<ExportReadListService> _logger) : IExportReadListService
1415
{
15-
public static Dictionary<string, Func<string, IRecordWriter<MangaStatus>>> GetWriters()
16+
public static Dictionary<string, Func<string, IRecordWriter<MangaStatus>>> GetWriters(string lang)
1617
{
1718
return new()
1819
{
19-
["csv"] = p => new CsvRecordWriter<MangaStatus, CsvManga>(p, CsvManga.FromManga),
20-
["json"] = p => new JsonRecordWriter<MangaStatus, CsvManga>(p, CsvManga.FromManga),
20+
["csv"] = p => new CsvRecordWriter<MangaStatus, CsvManga>(p, t => CsvManga.FromManga(t, lang)),
21+
["json"] = p => new JsonRecordWriter<MangaStatus, CsvManga>(p, t => CsvManga.FromManga(t, lang)),
2122
};
2223
}
2324

24-
public async IAsyncEnumerable<MangaStatus> GetUsersMangaStatus(ReadStatus? status,
25+
public async IAsyncEnumerable<MangaStatus> GetUsersMangaStatus(ReadStatus? status, bool includeChapters, string lang,
2526
[EnumeratorCancellation] CancellationToken token)
2627
{
2728
//Get all of the user's read statuses
@@ -52,22 +53,43 @@ public async IAsyncEnumerable<MangaStatus> GetUsersMangaStatus(ReadStatus? statu
5253
{
5354
if (token.IsCancellationRequested) yield break;
5455
var ms = readStatus.Statuses.TryGetValue(m.Id, out var s) ? s : ReadStatus.reading;
55-
yield return (m, ms);
56+
yield return await PopChapter(m, ms, includeChapters, lang, token);
5657
}
5758
}
5859
}
5960

60-
public async Task WriteUsersMangaStatusToFile(string file, ReadStatus? status, CancellationToken token)
61+
public async Task<MangaStatus> PopChapter(Manga manga, ReadStatus status, bool chaps, string lang, CancellationToken token)
62+
{
63+
if (!chaps) return (manga, status, null);
64+
65+
var filter = new ChaptersFilter
66+
{
67+
Manga = manga.Id,
68+
Limit = 1,
69+
TranslatedLanguage = string.IsNullOrEmpty(lang) ? [] : [lang],
70+
Order = new()
71+
{
72+
[ChaptersFilter.OrderKey.chapter] = OrderValue.desc
73+
},
74+
75+
};
76+
var chapters = await _api.Request(t => t.Chapter.List(filter), token);
77+
chapters.ThrowIfError();
78+
var chapter = chapters.Data.FirstOrDefault();
79+
return (manga, status, chapter);
80+
}
81+
82+
public async Task WriteUsersMangaStatusToFile(string file, ReadStatus? status, bool includeChapters, string lang, CancellationToken token)
6183
{
6284
var ext = Path.GetExtension(file).Trim('.').ToLower();
63-
if (!GetWriters().TryGetValue(ext, out var fetch))
85+
if (!GetWriters(lang).TryGetValue(ext, out var fetch))
6486
{
6587
_logger.LogError("No writer found for {Ext}", ext);
6688
return;
6789
}
6890

6991
using var writer = fetch(file);
70-
var manga = GetUsersMangaStatus(status, token);
92+
var manga = GetUsersMangaStatus(status, includeChapters, lang, token);
7193
await writer.Write(manga);
7294
_logger.LogInformation("Wrote read list to {File}", file);
7395
}
@@ -77,21 +99,51 @@ internal record class CsvManga(
7799
string Title,
78100
string CoverUrl,
79101
string TitleUrl,
80-
string Status)
102+
string Status,
103+
[property: JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] string? LatestChapterId,
104+
[property: JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] string? LatestChapterTitle,
105+
[property: JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] string? LatestChapterUrl)
81106
{
82-
public static CsvManga FromManga(MangaStatus m)
107+
public static CsvManga FromManga(MangaStatus m, string lang)
83108
{
109+
string? DetermineChapterTitle()
110+
{
111+
if (m.chapter is null) return null;
112+
113+
var bob = new StringBuilder();
114+
if (!string.IsNullOrEmpty(m.chapter.Attributes?.Volume))
115+
bob.Append($"Vol.{m.chapter.Attributes.Volume} ");
116+
if (!string.IsNullOrEmpty(m.chapter.Attributes?.Chapter))
117+
bob.Append($"Ch.{m.chapter.Attributes.Chapter} ");
118+
if (!string.IsNullOrEmpty(m.chapter.Attributes?.Title))
119+
bob.Append(m.chapter.Attributes.Title);
120+
return bob.ToString().Trim();
121+
}
122+
123+
string? DetermineChapterUrl()
124+
{
125+
if (m.chapter is null) return null;
126+
127+
if (!string.IsNullOrEmpty(m.chapter.Attributes?.ExternalUrl))
128+
return m.chapter.Attributes.ExternalUrl;
129+
130+
return $"https://mangadex.org/chapter/{m.chapter.Id}";
131+
}
132+
84133
var cover = m.manga.CoverArt().FirstOrDefault()?.Attributes;
85134
var coverUrl = cover is null
86135
? string.Empty
87136
: $"https://uploads.mangadex.org/covers/{m.manga.Id}/{cover.FileName}";
88137

89138
return new CsvManga(
90139
m.manga.Id,
91-
m.manga.Attributes?.Title?.PreferedOrFirst(t => t.Key == "en").Value ?? string.Empty,
140+
m.manga.Attributes?.Title?.PreferedOrFirst(t => t.Key == lang).Value ?? string.Empty,
92141
coverUrl,
93142
$"https://mangadex.org/title/{m.manga.Id}",
94-
m.status.ToString());
143+
m.status.ToString(),
144+
m.chapter?.Id,
145+
DetermineChapterTitle(),
146+
DetermineChapterUrl());
95147
}
96148
}
97149
}

src/MangaDexSharp.Utilities.Cli/Services/RateLimitService.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -108,11 +108,12 @@ public async Task<T> Request<T>(Func<IMangaDex, Task<T>> request, int current, C
108108
await EnsureNotLimited(token);
109109
var result = await request(_md);
110110

111+
if (result.RateLimit.HasRateLimits)
112+
_last = result.RateLimit;
113+
111114
if (!result.ErrorOccurred) return result;
112115

113116
var isTooMany = result.Errors.Any(e => e.Status == 429);
114-
if (result.RateLimit.HasRateLimits)
115-
_last = result.RateLimit;
116117

117118
if (isTooMany)
118119
return await Request(request, current + 1, token);

src/MangaDexSharp.Utilities.Cli/Verbs/ExportReadListVerb.cs

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,20 @@ namespace MangaDexSharp.Utilities.Cli.Verbs;
66
public class ExportReadListOptions : AuthOptions
77
{
88
private const string FILE_PATH = "read-list.json";
9+
private const bool INCLUDE_LATEST_CHAPTER = false;
10+
private const string PREFERRED_LANGUAGE = "en";
911

1012
[Option('f', "file-path", HelpText = "The file path to export to (supports csv or json)", Default = FILE_PATH)]
1113
public string FilePath { get; set; } = FILE_PATH;
14+
15+
[Option('i', "include-latest-chapter", HelpText = "Whether or not to include the latest chapter's name and URL in the output (This will be VERY slow if you have a large read history).", Default = INCLUDE_LATEST_CHAPTER)]
16+
public bool IncludeLatestChapter { get; set; } = INCLUDE_LATEST_CHAPTER;
17+
18+
[Option('r', "read-status", HelpText = "The read status to filter by (reading, on_hold, plan_to_read, dropped, re_reading, completed). Leaving empty will fetch all statuses", Default = null)]
19+
public string? ReadStatus { get; set; }
20+
21+
[Option('l', "preferred-language", HelpText = "The preferred language to use for the latest chapters and titles", Default = PREFERRED_LANGUAGE)]
22+
public string PreferredLanguage { get; set; } = PREFERRED_LANGUAGE;
1223
}
1324

1425
internal class ExportReadListVerb(
@@ -18,9 +29,19 @@ internal class ExportReadListVerb(
1829
{
1930
public override async Task<bool> Execute(ExportReadListOptions options, CancellationToken token)
2031
{
32+
ReadStatus? status = null;
33+
if (!string.IsNullOrEmpty(options.ReadStatus) &&
34+
Enum.TryParse<ReadStatus>(options.ReadStatus, true, out var result))
35+
status = result;
36+
2137
_cache.Auth = options;
2238
_logger.LogInformation("Writing read list to {FilePath}", options.FilePath);
23-
await _export.WriteUsersMangaStatusToFile(options.FilePath, null, token);
39+
await _export.WriteUsersMangaStatusToFile(
40+
options.FilePath,
41+
status,
42+
options.IncludeLatestChapter,
43+
options.PreferredLanguage,
44+
token);
2445
_logger.LogInformation("Finished writing read list to {FilePath}", options.FilePath);
2546
return true;
2647
}

src/MangaDexSharp.sln

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
2222
EndProject
2323
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MangaDexSharp.Utilities.Cli", "MangaDexSharp.Utilities.Cli\MangaDexSharp.Utilities.Cli.csproj", "{6048923B-711D-4375-A6D3-34449A0FD8CD}"
2424
EndProject
25+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Utilities", "Utilities", "{C2A9AE7D-B515-4C8C-80C3-C3786784D698}"
26+
EndProject
27+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{291B57E7-1976-458E-9FF0-6AF336418815}"
28+
EndProject
2529
Global
2630
GlobalSection(SolutionConfigurationPlatforms) = preSolution
2731
Debug|Any CPU = Debug|Any CPU
@@ -52,6 +56,12 @@ Global
5256
GlobalSection(SolutionProperties) = preSolution
5357
HideSolutionNode = FALSE
5458
EndGlobalSection
59+
GlobalSection(NestedProjects) = preSolution
60+
{E2BA6C78-048B-4649-A7B2-D28C2DC69708} = {291B57E7-1976-458E-9FF0-6AF336418815}
61+
{42298204-E183-426F-98CC-1FCB49AA1B6D} = {291B57E7-1976-458E-9FF0-6AF336418815}
62+
{70686F17-80CE-41E5-BEAB-6A32C61D195D} = {C2A9AE7D-B515-4C8C-80C3-C3786784D698}
63+
{6048923B-711D-4375-A6D3-34449A0FD8CD} = {C2A9AE7D-B515-4C8C-80C3-C3786784D698}
64+
EndGlobalSection
5565
GlobalSection(ExtensibilityGlobals) = postSolution
5666
SolutionGuid = {073A21EC-20C2-4268-910E-27C3E4B107CC}
5767
EndGlobalSection

src/MangaDexSharp/Helpers/FilterBuilder.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ public class FilterBuilder
88
/// <summary>
99
/// All of that parameters in this builder
1010
/// </summary>
11-
public List<(string key, string value)> Parameters { get; } = new();
11+
public List<(string key, string value)> Parameters { get; } = [];
1212

1313
/// <summary>
1414
/// Adds an optional string value to the parameters

src/MangaDexSharp/Helpers/MangaDexAggregateChapterParser.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,10 @@ public class MangaDexAggregateChapterParser : JsonConverter<Dictionary<string, M
1919
public override Dictionary<string, MangaAggregate.ChapterData>? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
2020
{
2121
if (reader.TokenType == JsonTokenType.StartObject)
22-
return JsonSerializer.Deserialize<Dictionary<string, MangaAggregate.ChapterData>>(ref reader, options) ?? new();
22+
return JsonSerializer.Deserialize<Dictionary<string, MangaAggregate.ChapterData>>(ref reader, options) ?? [];
2323

2424
if (reader.TokenType != JsonTokenType.StartArray)
25-
return new();
25+
return [];
2626

2727
var chapters = JsonSerializer.Deserialize<MangaAggregate.ChapterData[]>(ref reader, options);
2828
return chapters.ToDictionary(t => t.Chapter);

src/MangaDexSharp/Helpers/MangaDexDictionaryParser.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,10 @@ public class MangaDexDictionaryParser<TKey, TValue> : JsonConverter<Dictionary<T
2222
if (reader.TokenType == JsonTokenType.StartArray)
2323
{
2424
_ = JsonSerializer.Deserialize<string[]>(ref reader, options);
25-
return new();
25+
return [];
2626
}
2727

28-
return JsonSerializer.Deserialize<Dictionary<TKey, TValue>>(ref reader, options) ?? new();
28+
return JsonSerializer.Deserialize<Dictionary<TKey, TValue>>(ref reader, options) ?? [];
2929
}
3030

3131
/// <summary>
@@ -60,10 +60,10 @@ public class MangaDexDictionaryParser : JsonConverter<Localization>
6060
if (reader.TokenType == JsonTokenType.StartArray)
6161
{
6262
_ = JsonSerializer.Deserialize<string[]>(ref reader, options);
63-
return new Localization();
63+
return [];
6464
}
6565

66-
var dic = JsonSerializer.Deserialize<Dictionary<string, string>>(ref reader, options) ?? new();
66+
var dic = JsonSerializer.Deserialize<Dictionary<string, string>>(ref reader, options) ?? [];
6767

6868
var lcl = new Localization();
6969
foreach (var item in dic)

0 commit comments

Comments
 (0)