Skip to content

Commit 07fdb7d

Browse files
committed
Update icons; Use separate VM for music section
1 parent b355fa4 commit 07fdb7d

36 files changed

+997
-66
lines changed

src/Bible.Alarm.Shared/Models/Schedule/AlarmSchedule.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -404,6 +404,9 @@ int GetPublicationSortPriority(string code)
404404
throw new InvalidOperationException($"No tracks found in section {randomSection.SectionCode} for melody music publication {sample.Music.PublicationCode}");
405405
}
406406

407+
// Set the section code for the selected section
408+
sample.Music.SectionCode = randomSection.SectionCode;
409+
407410
// Select a random track from the selected section
408411
var randomTrack = randomSection.Tracks[Random.Shared.Next(randomSection.Tracks.Count)];
409412
sample.Music.TrackNumber = randomTrack.Number;

src/Bible.Alarm.Shared/Services/Media/BiblePublicationSectionService.cs

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,76 @@ public async Task<SortedDictionary<int, BiblePublicationSection>> GetSectionsByP
107107
}
108108
}
109109

110+
public async Task<SortedDictionary<int, BiblePublicationSection>> GetMusicSectionsByPublicationAsync(string publicationCode, CancellationToken cancellationToken = default)
111+
{
112+
try
113+
{
114+
using var scope = scopeFactory.CreateScope();
115+
var dbContext = scope.ServiceProvider.GetRequiredService<MediaDbContext>();
116+
117+
// Load sections for music publications (Category=Music, LanguageId=null)
118+
var sections = await dbContext.BiblePublications
119+
.AsNoTracking()
120+
.Include(x => x.Category)
121+
.Include(x => x.Sections)
122+
.ThenInclude(s => s.UrlParams)
123+
.Where(x => x.Category.CategoryName == "Music"
124+
&& x.LanguageId == null
125+
&& x.PublicationCode == publicationCode)
126+
.SelectMany(x => x.Sections)
127+
.ToListAsync(cancellationToken);
128+
129+
// Filter sections that have numeric SectionCode and order by it
130+
// For music sections like "iam-1", "iam-2", we need to handle both numeric and non-numeric codes
131+
var sectionsByNumber = new Dictionary<int, BiblePublicationSection>();
132+
foreach (var section in sections)
133+
{
134+
// Try to parse SectionCode as int (for numeric codes)
135+
if (int.TryParse(section.SectionCode, out var sectionNumber))
136+
{
137+
if (!sectionsByNumber.ContainsKey(sectionNumber))
138+
{
139+
sectionsByNumber[sectionNumber] = section;
140+
}
141+
}
142+
else
143+
{
144+
// For non-numeric codes like "iam-1", extract the number part
145+
// e.g., "iam-1" -> 1, "iam-2" -> 2
146+
var parts = section.SectionCode.Split('-');
147+
if (parts.Length > 1 && int.TryParse(parts[parts.Length - 1], out var extractedNumber))
148+
{
149+
if (!sectionsByNumber.ContainsKey(extractedNumber))
150+
{
151+
sectionsByNumber[extractedNumber] = section;
152+
}
153+
}
154+
else
155+
{
156+
// If we can't extract a number, use 0 as a fallback (will be sorted last)
157+
if (!sectionsByNumber.ContainsKey(0))
158+
{
159+
sectionsByNumber[0] = section;
160+
}
161+
}
162+
}
163+
}
164+
165+
// Sort by the extracted number
166+
var sortedSections = sectionsByNumber
167+
.OrderBy(kvp => kvp.Key)
168+
.ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
169+
170+
return new SortedDictionary<int, BiblePublicationSection>(sortedSections);
171+
}
172+
catch (Exception ex)
173+
{
174+
logger.Error(ex, "Error getting Music sections by publication. PublicationCode={PublicationCode}",
175+
publicationCode);
176+
throw;
177+
}
178+
}
179+
110180
public void Dispose()
111181
{
112182
if (isDisposed)

src/Bible.Alarm.Shared/Services/Media/Interfaces/IBiblePublicationSectionService.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,12 @@ public interface IBiblePublicationSectionService : IDisposable
2121
/// Gets all BiblePublicationSections for a given publication (by language code and publication code).
2222
/// </summary>
2323
Task<SortedDictionary<int, BiblePublicationSection>> GetSectionsByPublicationAsync(string languageCode, string publicationCode, CancellationToken cancellationToken = default);
24+
25+
/// <summary>
26+
/// Gets sections for music publications (Category=Music, LanguageId=null).
27+
/// Used for instrumental music like Kingdom Melodies.
28+
/// </summary>
29+
Task<SortedDictionary<int, BiblePublicationSection>> GetMusicSectionsByPublicationAsync(string publicationCode, CancellationToken cancellationToken = default);
2430

2531
/// <summary>
2632
/// Gets a BiblePublicationSection by language code, publication code, and section number.

src/Bible.Alarm.Shared/Services/Media/Interfaces/IMelodyMusicService.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,11 @@ public interface IMelodyMusicService : IDisposable
2626
/// Gets all tracks for a MelodyMusic release by publication code, with Source included.
2727
/// </summary>
2828
Task<SortedDictionary<int, MusicTrack>> GetTracksByCodeAsync(string publicationCode, CancellationToken cancellationToken = default);
29+
30+
/// <summary>
31+
/// Gets tracks for a specific section in a music publication (e.g., "iam-1" section in Kingdom Melodies).
32+
/// </summary>
33+
Task<SortedDictionary<int, MusicTrack>> GetTracksBySectionCodeAsync(string publicationCode, string sectionCode, CancellationToken cancellationToken = default);
2934

3035
/// <summary>
3136
/// Updates the URL for a MelodyMusic track's audio source.

src/Bible.Alarm.Shared/Services/Media/MelodyMusicService.cs

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,63 @@ public async Task<SortedDictionary<int, MusicTrack>> GetTracksByCodeAsync(string
202202
}
203203
}
204204

205+
public async Task<SortedDictionary<int, MusicTrack>> GetTracksBySectionCodeAsync(string publicationCode, string sectionCode, CancellationToken cancellationToken = default)
206+
{
207+
try
208+
{
209+
using var scope = scopeFactory.CreateScope();
210+
var dbContext = scope.ServiceProvider.GetRequiredService<MediaDbContext>();
211+
212+
// Get the publication with sections
213+
var publication = await dbContext.BiblePublications
214+
.AsNoTracking()
215+
.Include(x => x.Category)
216+
.Include(x => x.Sections)
217+
.ThenInclude(s => s.Tracks)
218+
.Where(x => x.Category.CategoryName == MusicCategoryName
219+
&& x.LanguageId == null
220+
&& x.PublicationCode == publicationCode)
221+
.FirstOrDefaultAsync(cancellationToken);
222+
223+
if (publication == null)
224+
{
225+
return new SortedDictionary<int, MusicTrack>();
226+
}
227+
228+
// Find the section by section code
229+
var section = publication.Sections?.FirstOrDefault(s =>
230+
s.SectionCode != null &&
231+
s.SectionCode.Equals(sectionCode, StringComparison.OrdinalIgnoreCase));
232+
233+
if (section == null || section.Tracks == null || section.Tracks.Count == 0)
234+
{
235+
return new SortedDictionary<int, MusicTrack>();
236+
}
237+
238+
// Map BiblePublicationTrack to MusicTrack
239+
var musicTracks = section.Tracks
240+
.OrderBy(t => t.Number)
241+
.Select(t => new MusicTrack
242+
{
243+
Number = t.Number,
244+
Title = t.Title,
245+
Url = string.Empty, // URLs are computed on-demand
246+
LookUpPath = string.Empty,
247+
DownloadCode = null,
248+
OriginalTrackNumber = null
249+
})
250+
.ToDictionary(x => x.Number, x => x);
251+
252+
return new SortedDictionary<int, MusicTrack>(musicTracks);
253+
}
254+
catch (Exception ex)
255+
{
256+
logger.Error(ex, "Error getting MelodyMusic tracks by section. PublicationCode={PublicationCode}, SectionCode={SectionCode}",
257+
publicationCode, sectionCode);
258+
throw;
259+
}
260+
}
261+
205262
public async Task UpdateTrackUrlAsync(string publicationCode, int trackNumber, string url, CancellationToken cancellationToken = default)
206263
{
207264
// URLs are now computed on-demand, no need to store them

src/Bible.Alarm/Common/Helpers/ServiceRegistrationHelper.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,7 @@ private static void RegisterViewModels(IServiceCollection services)
306306
services.AddTransient<CategorySelectionViewModel>();
307307
services.AddTransient<SectionSelectionViewModel>();
308308
services.AddTransient<ViewModels.BiblePublications.TrackSelectionViewModel>();
309+
services.AddTransient<ViewModels.Music.MusicSectionSelectionViewModel>();
309310
services.AddTransient<AlarmViewModel>();
310311
services.AddTransient<BiblePublicationSelectionContainerViewModel>();
311312
services.AddTransient<MusicSelectionContainerViewModel>();
@@ -344,6 +345,7 @@ private static void RegisterUiComponents(IServiceCollection services)
344345
services.AddTransient<BiblePublicationSelectionModal>();
345346
services.AddTransient<SectionSelectionModal>();
346347
services.AddTransient<Views.Music.TrackSelectionModal>();
348+
services.AddTransient<Views.Music.MusicSectionSelectionModal>();
347349
services.AddTransient<MusicSelectionModal>();
348350
services.AddTransient<SongPublicationSelectionModal>();
349351
services.AddTransient<Views.Bible.TrackSelectionModal>();

src/Bible.Alarm/Services/Media/BiblePublicationNavigationService.cs

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
#nullable enable
12
using Bible.Alarm.Services.Media.Interfaces;
23
using Bible.Alarm.Shared.Models.Schedule;
34
using Serilog;
@@ -30,12 +31,6 @@ private async Task<int> ConvertSectionCodeToIntAsync(string? sectionCode, string
3031
// If parsing fails, SectionCode is not numeric (e.g., "gen" for Genesis)
3132
// For non-numeric section codes, return 0
3233
return 0;
33-
{
34-
logger.Warning(ex, "Error converting SectionCode {SectionCode} to int for {LanguageCode}/{PublicationCode}",
35-
sectionCode, languageCode, publicationCode);
36-
}
37-
38-
return 0;
3934
}
4035

4136
public async Task<bool> MoveToPreviousSectionAsync(BiblePublicationSchedule schedule)

src/Bible.Alarm/Services/Media/Interfaces/IMediaService.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,13 @@ public interface IMediaService : IDisposable
99
Task<Dictionary<string, Language>> GetBiblePublicationLanguages();
1010
Task<Dictionary<string, BiblePublication>> GetBiblePublications(string languageCode);
1111
Task<SortedDictionary<int, BiblePublicationSection>> GetBiblePublicationSections(string languageCode, string versionCode);
12+
Task<SortedDictionary<int, BiblePublicationSection>> GetMusicSections(string publicationCode);
1213
Task<BiblePublicationSection> GetBiblePublicationSection(string languageCode, string versionCode, int sectionNumber);
1314
Task<SortedDictionary<int, BiblePublicationTrack>> GetBiblePublicationTracks(string languageCode, string versionCode, int sectionNumber);
1415
Task<BiblePublicationTrack> GetBiblePublicationTrack(string languageCode, string versionCode, int sectionNumber, int trackNumber);
1516
Task<Dictionary<string, MelodyMusic>> GetMelodyMusicReleases();
1617
Task<SortedDictionary<int, MusicTrack>> GetMelodyMusicTracks(string publicationCode);
18+
Task<SortedDictionary<int, MusicTrack>> GetMelodyMusicTracksBySection(string publicationCode, string sectionCode);
1719
Task<Dictionary<string, Language>> GetVocalMusicLanguages();
1820
Task<Dictionary<string, VocalMusic>> GetVocalMusicReleases(string languageCode);
1921
Task<SortedDictionary<int, MusicTrack>> GetVocalMusicTracks(string languageCode, string publicationCode);

src/Bible.Alarm/Services/Media/MediaService.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,12 @@ public async Task<SortedDictionary<int, BiblePublicationSection>> GetBiblePublic
4545
return await biblePublicationSectionService.GetSectionsByPublicationAsync(languageCode, versionCode, cancellationTokenSource.Token);
4646
}
4747

48+
public async Task<SortedDictionary<int, BiblePublicationSection>> GetMusicSections(string publicationCode)
49+
{
50+
await mediaIndexService.Verify();
51+
return await biblePublicationSectionService.GetMusicSectionsByPublicationAsync(publicationCode, cancellationTokenSource.Token);
52+
}
53+
4854
public async Task<BiblePublicationSection> GetBiblePublicationSection(string languageCode, string versionCode, int sectionNumber)
4955
{
5056
await mediaIndexService.Verify();
@@ -78,6 +84,12 @@ public async Task<SortedDictionary<int, MusicTrack>>
7884
return await melodyMusicService.GetTracksByCodeAsync(publicationCode, cancellationTokenSource.Token);
7985
}
8086

87+
public async Task<SortedDictionary<int, MusicTrack>> GetMelodyMusicTracksBySection(string publicationCode, string sectionCode)
88+
{
89+
await mediaIndexService.Verify();
90+
return await melodyMusicService.GetTracksBySectionCodeAsync(publicationCode, sectionCode, cancellationTokenSource.Token);
91+
}
92+
8193
public async Task<Dictionary<string, Language>> GetVocalMusicLanguages()
8294
{
8395
await mediaIndexService.Verify();

src/Bible.Alarm/Services/Media/Playlist/PlaylistBibleTrackBuilder.cs

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -57,11 +57,6 @@ private async Task<int> ConvertSectionCodeToIntAsync(string? sectionCode, string
5757
// If parsing fails, SectionCode is not numeric (e.g., "gen" for Genesis)
5858
// For non-numeric section codes, return 0
5959
return 0;
60-
61-
// Fallback: return 0 if section not found
62-
logger.Warning("[PlaylistBuild] Could not convert SectionCode {SectionCode} to int for {LanguageCode}/{PublicationCode}, using 0",
63-
sectionCode, languageCode, publicationCode);
64-
return 0;
6560
}
6661

6762
public async Task<List<PlayItem>> BuildBiblePublicationTracks(

0 commit comments

Comments
 (0)