Skip to content

Commit 27b0bb9

Browse files
authored
Spontify playlist (#4)
1 parent fb0e70e commit 27b0bb9

File tree

18 files changed

+788
-15
lines changed

18 files changed

+788
-15
lines changed

.github/workflows/scrape.yaml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,19 @@ jobs:
6868
$result | Out-File -FilePath "daily100-$date.json" -Force
6969
popd
7070
71+
- name: Run Spotify app - Daily 100
72+
shell: pwsh
73+
run: |
74+
$date = (Get-Date).ToUniversalTime().AddHours(9).ToString("yyyyMMdd")
75+
# $result = dotnet run --project ./samples/SpotifyPlaylist.ConsoleApp/ -- -t spotify -s ${{ secrets.SPOTIFY_PLAYLIST_ID }} --json | ConvertFrom-Json
76+
$result = dotnet run --project ./samples/SpotifyPlaylist.ConsoleApp/ -- -t spotify -s ${{ secrets.SPOTIFY_PLAYLIST_ID }} --json
77+
78+
mkdir -p ./data
79+
pushd ./data
80+
# $result | ConvertTo-Json -Depth 100 | Out-File -FilePath "spotify100-$date.json" -Force
81+
$result | Out-File -FilePath "spotify100-$date.json" -Force
82+
popd
83+
7184
- name: Upload data
7285
uses: stefanzweifel/git-auto-commit-action@v5
7386
with:

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -482,3 +482,7 @@ $RECYCLE.BIN/
482482

483483
# Vim temporary swap files
484484
*.swp
485+
486+
# project specific
487+
appsettings.*.json
488+
!appsettings.*.sample.json

MelonChart.sln

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{ACA1
1515
EndProject
1616
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MelonChart.ConsoleApp", "samples\MelonChart.ConsoleApp\MelonChart.ConsoleApp.csproj", "{FA517387-3495-4649-81D3-AF51A9A683B8}"
1717
EndProject
18-
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MelonChart.WebApp", "samples\MelonChart.WebApp\MelonChart.WebApp.csproj", "{1515CA58-4FAF-4F89-898C-599E55694F84}"
18+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MelonChart.WebApp", "samples\MelonChart.WebApp\MelonChart.WebApp.csproj", "{1515CA58-4FAF-4F89-898C-599E55694F84}"
19+
EndProject
20+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SpotifyPlaylist.ConsoleApp", "samples\SpotifyPlaylist.ConsoleApp\SpotifyPlaylist.ConsoleApp.csproj", "{82E64E82-F366-4650-9A66-E15FAF968882}"
1921
EndProject
2022
Global
2123
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -39,6 +41,10 @@ Global
3941
{1515CA58-4FAF-4F89-898C-599E55694F84}.Debug|Any CPU.Build.0 = Debug|Any CPU
4042
{1515CA58-4FAF-4F89-898C-599E55694F84}.Release|Any CPU.ActiveCfg = Release|Any CPU
4143
{1515CA58-4FAF-4F89-898C-599E55694F84}.Release|Any CPU.Build.0 = Release|Any CPU
44+
{82E64E82-F366-4650-9A66-E15FAF968882}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
45+
{82E64E82-F366-4650-9A66-E15FAF968882}.Debug|Any CPU.Build.0 = Debug|Any CPU
46+
{82E64E82-F366-4650-9A66-E15FAF968882}.Release|Any CPU.ActiveCfg = Release|Any CPU
47+
{82E64E82-F366-4650-9A66-E15FAF968882}.Release|Any CPU.Build.0 = Release|Any CPU
4248
EndGlobalSection
4349
GlobalSection(SolutionProperties) = preSolution
4450
HideSolutionNode = FALSE
@@ -48,6 +54,7 @@ Global
4854
{48FBE532-F50C-4CE5-A61D-86B5921B9ACE} = {8CF3DBA1-84DB-4274-86EA-3BB54B7262C0}
4955
{FA517387-3495-4649-81D3-AF51A9A683B8} = {ACA1464B-2D70-4B57-B6A6-2E399931AC0A}
5056
{1515CA58-4FAF-4F89-898C-599E55694F84} = {ACA1464B-2D70-4B57-B6A6-2E399931AC0A}
57+
{82E64E82-F366-4650-9A66-E15FAF968882} = {ACA1464B-2D70-4B57-B6A6-2E399931AC0A}
5158
EndGlobalSection
5259
GlobalSection(ExtensibilityGlobals) = postSolution
5360
SolutionGuid = {34032734-030B-4982-8F04-9138EFCDF681}

samples/MelonChart.ConsoleApp/Services/MelonChartService.cs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,16 +14,20 @@ namespace MelonChart.ConsoleApp.Services;
1414
/// <param name="charts">List of <see cref="IChart"/> instances.</param>
1515
public class MelonChartService(IEnumerable<IChart> charts) : IMelonChartService
1616
{
17-
private readonly IEnumerable<IChart> _charts = charts ?? throw new ArgumentNullException(nameof(charts));
17+
#pragma warning disable IDE1006 // Naming Styles
1818

19-
private static JsonSerializerOptions jso = new()
19+
private static readonly JsonSerializerOptions jso = new()
2020
{
2121
WriteIndented = true,
2222
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
2323
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
2424
Converters = { new JsonStringEnumConverter(JsonNamingPolicy.CamelCase) },
2525
};
2626

27+
#pragma warning restore IDE1006 // Naming Styles
28+
29+
private readonly IEnumerable<IChart> _charts = charts ?? throw new ArgumentNullException(nameof(charts));
30+
2731
/// <inheritdoc />
2832
public async Task RunAsync(string[] args)
2933
{
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
namespace SpotifyPlaylist.ConsoleApp.Configs;
2+
3+
/// <summary>
4+
/// This represents the app settings entity for Azure.
5+
/// </summary>
6+
public class AzureSettings
7+
{
8+
/// <summary>
9+
/// Define the name of the settings.
10+
/// </summary>
11+
public const string Name = "Azure";
12+
13+
/// <summary>
14+
/// Gets or sets the <see cref="ApiManagementSettings"/> instance.
15+
/// </summary>
16+
public ApiManagementSettings? APIM { get; set; }
17+
}
18+
19+
/// <summary>
20+
/// This represents the app settings entity for API Management.
21+
/// </summary>
22+
public class ApiManagementSettings
23+
{
24+
/// <summary>
25+
/// Define the name of the settings.
26+
/// </summary>
27+
public const string Name = "APIM";
28+
29+
/// <summary>
30+
/// Gets or sets the base URL.
31+
/// </summary>
32+
public string? BaseUrl { get; set; }
33+
34+
/// <summary>
35+
/// Gets or sets the subscription key.
36+
/// </summary>
37+
public string? SubscriptionKey { get; set; }
38+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
namespace SpotifyPlaylist.ConsoleApp.Configs;
2+
3+
/// <summary>
4+
/// This represents the app settings entity for Spotify.
5+
/// </summary>
6+
public class SpotifySettings
7+
{
8+
/// <summary>
9+
/// Define the name of the settings.
10+
/// </summary>
11+
public const string Name = "Spotify";
12+
13+
/// <summary>
14+
/// Gets or sets the market. Default is "KR".
15+
/// </summary>
16+
public string? Market { get; set; } = "KR";
17+
18+
/// <summary>
19+
/// Gets or sets the maximum number of items to return. Default is 10.
20+
/// </summary>
21+
public int? MaxItems { get; set; } = 10;
22+
23+
/// <summary>
24+
/// Gets or sets the time zone ID. Default is "Asia/Seoul".
25+
/// </summary>
26+
public string? TimeZoneId { get; set; } = "Asia/Seoul";
27+
28+
/// <summary>
29+
/// Gets or sets the <see cref="SpotifyPlaylistSettings"/> instance.
30+
/// </summary>
31+
public SpotifyPlaylistSettings? Playlist { get; set; }
32+
}
33+
34+
/// <summary>
35+
/// This represents the app settings entity for Spotify playlist.
36+
/// </summary>
37+
public class SpotifyPlaylistSettings
38+
{
39+
/// <summary>
40+
/// Gets or sets the name of the playlist.
41+
/// </summary>
42+
public string? Name { get; set; }
43+
44+
/// <summary>
45+
/// Gets or sets the description of the playlist.
46+
/// </summary>
47+
public string? Description { get; set; }
48+
49+
/// <summary>
50+
/// Gets or sets the cover image URL of the playlist.
51+
/// </summary>
52+
public string? CoverImageUrl { get; set; }
53+
}
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
namespace SpotifyPlaylist.ConsoleApp.Options;
2+
3+
/// <summary>
4+
/// This represents the options entity for arguments.
5+
/// </summary>
6+
public class ArgumentOptions
7+
{
8+
/// <summary>
9+
/// Gets or sets the source that contains the Melon Chart data.
10+
/// </summary>
11+
public string? Source { get; set; }
12+
13+
/// <summary>
14+
/// Gets or sets the source type.
15+
/// </summary>
16+
public SourceType SourceType { get; set; }
17+
18+
/// <summary>
19+
/// Gets or sets the query to search for.
20+
/// </summary>
21+
public string? Query { get; set; }
22+
23+
/// <summary>
24+
/// Gets or sets the value indicating whether to output as JSON or not.
25+
/// </summary>
26+
public bool OutputAsJson { get; set; } = false;
27+
28+
/// <summary>
29+
/// Gets or sets the value indicating whether to display help or not.
30+
/// </summary>
31+
public bool Help { get; set; } = false;
32+
33+
/// <summary>
34+
/// Parses the arguments and returns the <see cref="ArgumentOptions"/> instance.
35+
/// </summary>
36+
/// <param name="args">List of arguments.</param>
37+
/// <returns>Returns the <see cref="ArgumentOptions"/> instance.</returns>
38+
public static ArgumentOptions Parse(string[] args)
39+
{
40+
var options = new ArgumentOptions();
41+
if (args.Length == 0)
42+
{
43+
return options;
44+
}
45+
46+
for (var i = 0; i < args.Length; i++)
47+
{
48+
var arg = args[i];
49+
switch (arg)
50+
{
51+
case "-t":
52+
case "--source-type":
53+
if (i < args.Length - 1)
54+
{
55+
options.SourceType = Enum.TryParse<SourceType>(args[++i], ignoreCase: true, out var result)
56+
? result
57+
: SourceType.Undefined;
58+
}
59+
break;
60+
61+
case "-s":
62+
case "--source":
63+
if (i < args.Length - 1)
64+
{
65+
options.Source = args[++i];
66+
}
67+
break;
68+
69+
case "-q":
70+
case "--query":
71+
if (i < args.Length - 1)
72+
{
73+
options.Query = args[++i];
74+
}
75+
break;
76+
77+
case "--json":
78+
options.OutputAsJson = true;
79+
break;
80+
81+
case "-h":
82+
case "--help":
83+
options.Help = true;
84+
break;
85+
}
86+
}
87+
88+
return options;
89+
}
90+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
namespace SpotifyPlaylist.ConsoleApp.Options;
2+
3+
/// <summary>
4+
/// This defines the source type enumeration.
5+
/// </summary>
6+
public enum SourceType
7+
{
8+
/// <summary>
9+
/// Identifies undefined source type.
10+
/// </summary>
11+
Undefined,
12+
13+
/// <summary>
14+
/// Identifies Melon Chart.
15+
/// </summary>
16+
Melon,
17+
18+
/// <summary>
19+
/// Identifies Spotify.
20+
/// </summary>
21+
Spotify
22+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
using Microsoft.Extensions.Configuration;
2+
using Microsoft.Extensions.DependencyInjection;
3+
using Microsoft.Extensions.Hosting;
4+
5+
using SpotifyPlaylist.ConsoleApp.Configs;
6+
using SpotifyPlaylist.ConsoleApp.Services;
7+
8+
var host = Host.CreateDefaultBuilder(args)
9+
.UseConsoleLifetime()
10+
.ConfigureServices(services =>
11+
{
12+
var config = new ConfigurationBuilder()
13+
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
14+
.AddJsonFile($"appsettings.Development.json", optional: true, reloadOnChange: true)
15+
.Build();
16+
var apim = config.GetSection(AzureSettings.Name).GetSection(ApiManagementSettings.Name).Get<ApiManagementSettings>();
17+
var spotify = config.GetSection("Spotify").Get<SpotifySettings>();
18+
19+
services.AddSingleton(spotify!);
20+
21+
services.AddHttpClient<ISpotifyPlaylistService, SpotifyPlaylistService>((services, http) =>
22+
{
23+
http.BaseAddress = new Uri(apim?.BaseUrl!);
24+
http.DefaultRequestHeaders.Add("Ocp-Apim-Subscription-Key", apim?.SubscriptionKey!);
25+
});
26+
})
27+
.Build();
28+
29+
var service = host.Services.GetRequiredService<ISpotifyPlaylistService>();
30+
await service.RunAsync(args);
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
using System.Runtime.CompilerServices;
2+
3+
namespace SpotifyPlaylist.ConsoleApp;
4+
5+
/// <summary>
6+
/// This represents the project path information entity.
7+
/// </summary>
8+
/// <remarks>Reference: https://stackoverflow.com/questions/816566/how-do-you-get-the-current-project-directory-from-c-sharp-code-when-creating-a-c#answer-68864779</remarks>
9+
internal static class ProjectPathInfo
10+
{
11+
public static string CSharpClassFileName = nameof(ProjectPathInfo) + ".cs";
12+
public static string CSharpClassPath;
13+
public static string ProjectPath;
14+
15+
/// <summary>
16+
/// Initializes static members of the <see cref="ProjectPathInfo"/> class.
17+
/// </summary>
18+
static ProjectPathInfo()
19+
{
20+
CSharpClassPath = GetSourceFilePathName();
21+
ProjectPath = Directory.GetParent(CSharpClassPath)!.FullName;
22+
}
23+
24+
/// <summary>
25+
/// Gets the source file path name.
26+
/// </summary>
27+
/// <param name="callerFilePath">File path of the caller.</param>
28+
/// <returns>Returns the file path of the caller.</returns>
29+
private static string GetSourceFilePathName([CallerFilePath] string? callerFilePath = null) => callerFilePath ?? "";
30+
}

0 commit comments

Comments
 (0)