3 Commits
2 changed files with 154 additions and 146 deletions
+150 -142
View File
@@ -25,6 +25,8 @@ namespace RadioDJViewer
{
private string _currentTrackKey = string.Empty;
private string _playbackTrackKey = string.Empty;
private string _previousToastTitle = string.Empty;
private string _previousToastArtist = string.Empty;
private double _playbackElapsedSeconds = 0;
private double _playbackDurationSeconds = 0;
private bool _playbackPaused = true;
@@ -152,114 +154,110 @@ namespace RadioDJViewer
// Use the label's actual width and font to estimate visible characters
using (Graphics g = label.CreateGraphics())
{
// Use the full label width for measurement
SizeF size = g.MeasureString("W", label.Font);
int chars = (int)(label.Width / size.Width);
// If the label is single-line, ensure we use the full width
return Math.Max(chars, 1);
}
}
private bool ShouldScroll(Label label, string text)
{
return !string.IsNullOrWhiteSpace(text) && text.Length > GetVisibleChars(label);
}
private string GetMarqueeWindow(string source, int startIndex, int length)
{
if (string.IsNullOrEmpty(source) || length <= 0)
return string.Empty;
var builder = new StringBuilder(length);
for (int i = 0; i < length; i++)
{
builder.Append(source[(startIndex + i) % source.Length]);
}
return builder.ToString();
}
private void SetLabelTextFull(Label label, string text)
{
// Always set the full text, and let marquee logic handle scrolling
label.Text = text;
}
private void StartMarqueeTimers()
{
marqueeOffsetTitle = 0;
marqueeOffsetArtist = 0;
marqueeOffsetAlbum = 0;
if (pauseTimerTitle != null) pauseTimerTitle.Stop();
if (pauseTimerArtist != null) pauseTimerArtist.Stop();
if (pauseTimerAlbum != null) pauseTimerAlbum.Stop();
if (ShouldScroll(label4, marqueeTextTitle)) marqueeTimerTitle.Start(); else marqueeTimerTitle.Stop();
if (ShouldScroll(label5, marqueeTextArtist)) marqueeTimerArtist.Start(); else marqueeTimerArtist.Stop();
if (ShouldScroll(label6, marqueeTextAlbum)) marqueeTimerAlbum.Start(); else marqueeTimerAlbum.Stop();
}
private void UpdateMarqueeLabel(Label label, System.Windows.Forms.Timer marqueeTimer, ref System.Windows.Forms.Timer pauseTimer, string text, string labelName, Func<int> getOffset, Action<int> setOffset, Action resetOffset)
{
string displayText = string.IsNullOrWhiteSpace(text) ? string.Empty : text;
int visibleChars = GetVisibleChars(label);
string separator = string.IsNullOrEmpty(marqueeSeparator) ? string.Empty : marqueeSeparator;
int offset = getOffset();
if (!ShouldScroll(label, displayText))
{
marqueeTimer.Stop();
if (pauseTimer != null) pauseTimer.Stop();
resetOffset();
SetLabelTextFull(label, displayText);
return;
}
string scrollText = displayText + separator;
if (offset >= scrollText.Length)
offset = 0;
label.Text = GetMarqueeWindow(scrollText, offset, visibleChars);
LogMessage($"{labelName}: separator='{separator}', scrollText='{scrollText}', offset={offset}");
if (offset >= displayText.Length)
{
marqueeTimer.Stop();
var pause = pauseTimer;
if (pause == null)
{
pause = new System.Windows.Forms.Timer();
pause.Tick += (s, args) =>
{
pause.Stop();
resetOffset();
marqueeTimer.Start();
};
pauseTimer = pause;
}
pause.Interval = marqueePauseTime;
pause.Start();
return;
}
setOffset(offset + 1);
}
private void MarqueeTimerTitle_Tick(object sender, EventArgs e)
{
int visibleChars = GetVisibleChars(label4);
if (marqueeTextTitle.Length > visibleChars)
{
string separator = string.IsNullOrEmpty(marqueeSeparator) ? "" : marqueeSeparator;
string scrollText = marqueeTextTitle + separator;
string repeatedScrollText = scrollText + scrollText;
marqueeOffsetTitle = (marqueeOffsetTitle + 1) % scrollText.Length;
label4.Text = repeatedScrollText.Substring(marqueeOffsetTitle, visibleChars);
LogMessage($"MarqueeTitle: separator='{separator}', scrollText='{scrollText}', offset={marqueeOffsetTitle}");
if (marqueeOffsetTitle == 0)
{
marqueeTimerTitle.Stop();
if (pauseTimerTitle == null)
{
pauseTimerTitle = new System.Windows.Forms.Timer();
pauseTimerTitle.Tick += (s, args) => {
pauseTimerTitle.Stop();
marqueeTimerTitle.Start();
};
}
pauseTimerTitle.Interval = marqueePauseTime;
pauseTimerTitle.Start();
}
}
else
{
SetLabelTextFull(label4, marqueeTextTitle);
}
UpdateMarqueeLabel(label4, marqueeTimerTitle, ref pauseTimerTitle, marqueeTextTitle, "MarqueeTitle", () => marqueeOffsetTitle, value => marqueeOffsetTitle = value, () => marqueeOffsetTitle = 0);
}
private void MarqueeTimerArtist_Tick(object sender, EventArgs e)
{
int visibleChars = GetVisibleChars(label5);
if (marqueeTextArtist.Length > visibleChars)
{
string separator = string.IsNullOrEmpty(marqueeSeparator) ? "" : marqueeSeparator;
string scrollText = marqueeTextArtist + separator;
string repeatedScrollText = scrollText + scrollText;
marqueeOffsetArtist = (marqueeOffsetArtist + 1) % scrollText.Length;
label5.Text = repeatedScrollText.Substring(marqueeOffsetArtist, visibleChars);
LogMessage($"MarqueeArtist: separator='{separator}', scrollText='{scrollText}', offset={marqueeOffsetArtist}");
if (marqueeOffsetArtist == 0)
{
marqueeTimerArtist.Stop();
if (pauseTimerArtist == null)
{
pauseTimerArtist = new System.Windows.Forms.Timer();
pauseTimerArtist.Tick += (s, args) => {
pauseTimerArtist.Stop();
marqueeTimerArtist.Start();
};
}
pauseTimerArtist.Interval = marqueePauseTime;
pauseTimerArtist.Start();
}
}
else
{
SetLabelTextFull(label5, marqueeTextArtist);
}
UpdateMarqueeLabel(label5, marqueeTimerArtist, ref pauseTimerArtist, marqueeTextArtist, "MarqueeArtist", () => marqueeOffsetArtist, value => marqueeOffsetArtist = value, () => marqueeOffsetArtist = 0);
}
private void MarqueeTimerAlbum_Tick(object sender, EventArgs e)
{
int visibleChars = GetVisibleChars(label6);
if (marqueeTextAlbum.Length > visibleChars)
{
string separator = string.IsNullOrEmpty(marqueeSeparator) ? "" : marqueeSeparator;
string scrollText = marqueeTextAlbum + separator;
string repeatedScrollText = scrollText + scrollText;
marqueeOffsetAlbum = (marqueeOffsetAlbum + 1) % scrollText.Length;
label6.Text = repeatedScrollText.Substring(marqueeOffsetAlbum, visibleChars);
LogMessage($"MarqueeAlbum: separator='{separator}', scrollText='{scrollText}', offset={marqueeOffsetAlbum}");
if (marqueeOffsetAlbum == 0)
{
marqueeTimerAlbum.Stop();
if (pauseTimerAlbum == null)
{
pauseTimerAlbum = new System.Windows.Forms.Timer();
pauseTimerAlbum.Tick += (s, args) => {
pauseTimerAlbum.Stop();
marqueeTimerAlbum.Start();
};
}
pauseTimerAlbum.Interval = marqueePauseTime;
pauseTimerAlbum.Start();
}
}
else
{
SetLabelTextFull(label6, marqueeTextAlbum);
}
UpdateMarqueeLabel(label6, marqueeTimerAlbum, ref pauseTimerAlbum, marqueeTextAlbum, "MarqueeAlbum", () => marqueeOffsetAlbum, value => marqueeOffsetAlbum = value, () => marqueeOffsetAlbum = 0);
}
private void ApiTimer_Tick(object sender, EventArgs e)
@@ -293,38 +291,6 @@ namespace RadioDJViewer
var xml = await response.Content.ReadAsStringAsync();
lastApiXml = xml;
ParseAndDisplaySongInfo(xml);
try
{
var newKey = $"{marqueeTextArtist}|{marqueeTextTitle}";
if (!string.Equals(newKey, _currentTrackKey, StringComparison.Ordinal))
{
_currentTrackKey = newKey;
ShowTemporaryStatus("Song Change Detected", 2000);
bool toastEnabled = IsToastEnabledForActiveProfile();
#if DEBUG
System.Diagnostics.Debug.WriteLine($"[Toast] Track changed. Enabled={toastEnabled}, Profile={loadedProfile?.Name ?? "(null)"}");
#endif
if (toastEnabled)
{
try
{
string outputImagePath = GetProfileOutputImagePath();
#if DEBUG
System.Diagnostics.Debug.WriteLine($"[Toast] Trigger with title='{marqueeTextTitle}', artist='{marqueeTextArtist}', imagePath='{outputImagePath}'");
#endif
ShowTrackChangeToast(marqueeTextTitle, marqueeTextArtist, outputImagePath);
}
catch (Exception ex)
{
#if DEBUG
System.Diagnostics.Debug.WriteLine($"[Toast] Trigger error: {ex}");
MessageBox.Show($"Toast trigger failed: {ex.Message}", "Toast Debug", MessageBoxButtons.OK, MessageBoxIcon.Warning);
#endif
}
}
}
}
catch { }
}
}
catch { }
@@ -524,16 +490,6 @@ namespace RadioDJViewer
}
}
private void StartMarqueeTimers()
{
marqueeOffsetTitle = 0;
marqueeOffsetArtist = 0;
marqueeOffsetAlbum = 0;
marqueeTimerTitle.Start();
marqueeTimerArtist.Start();
marqueeTimerAlbum.Start();
}
private void StopMarqueeTimers()
{
marqueeTimerTitle.Stop();
@@ -700,6 +656,8 @@ namespace RadioDJViewer
{
UpdateCurrentSongImage(null);
}
HandleToastForTrackChange(title, artist);
}
catch
{
@@ -1009,6 +967,67 @@ namespace RadioDJViewer
return loadedProfile != null && loadedProfile.ToastNotificationsEnabled;
}
private void HandleToastForTrackChange(string title, string artist)
{
string normalizedTitle = NormalizeTrackMetadata(title);
string normalizedArtist = NormalizeTrackMetadata(artist);
if (string.Equals(normalizedTitle, _previousToastTitle, StringComparison.OrdinalIgnoreCase) &&
string.Equals(normalizedArtist, _previousToastArtist, StringComparison.OrdinalIgnoreCase))
{
return;
}
_previousToastTitle = normalizedTitle;
_previousToastArtist = normalizedArtist;
if (!IsToastEnabledForActiveProfile())
return;
if (!HasValidTrackMetadata(normalizedTitle, normalizedArtist))
return;
try
{
string outputImagePath = GetProfileOutputImagePath();
#if DEBUG
System.Diagnostics.Debug.WriteLine($"[Toast] Track change accepted. title='{normalizedTitle}', artist='{normalizedArtist}', imagePath='{outputImagePath}'");
#endif
ShowTrackChangeToast(normalizedTitle, normalizedArtist, outputImagePath);
}
catch (Exception ex)
{
#if DEBUG
System.Diagnostics.Debug.WriteLine($"[Toast] Trigger error: {ex}");
MessageBox.Show($"Toast trigger failed: {ex.Message}", "Toast Debug", MessageBoxButtons.OK, MessageBoxIcon.Warning);
#endif
}
}
private static string NormalizeTrackMetadata(string value)
{
return string.IsNullOrWhiteSpace(value) ? string.Empty : value.Trim();
}
private static bool HasValidTrackMetadata(string title, string artist)
{
return IsValidToastValue(title) && IsValidToastValue(artist);
}
private static bool IsValidToastValue(string value)
{
if (string.IsNullOrWhiteSpace(value))
return false;
string normalized = value.Trim();
return !normalized.Equals("No Title", StringComparison.OrdinalIgnoreCase)
&& !normalized.Equals("No Artist", StringComparison.OrdinalIgnoreCase)
&& !normalized.Equals("Unknown", StringComparison.OrdinalIgnoreCase)
&& !normalized.Equals("No title", StringComparison.OrdinalIgnoreCase)
&& !normalized.Equals("No artist", StringComparison.OrdinalIgnoreCase)
&& !normalized.Equals("unknown", StringComparison.OrdinalIgnoreCase);
}
private string GetProfileOutputImagePath()
{
if (string.IsNullOrWhiteSpace(outputFolderPath) || string.IsNullOrWhiteSpace(loadedProfile?.OutputImageName))
@@ -1017,8 +1036,7 @@ namespace RadioDJViewer
try
{
string imageName = Path.ChangeExtension(loadedProfile.OutputImageName, ".png");
string fullPath = Path.GetFullPath(Path.Combine(outputFolderPath, imageName));
return fullPath;
return Path.GetFullPath(Path.Combine(outputFolderPath, imageName));
}
catch
{
@@ -1032,17 +1050,7 @@ namespace RadioDJViewer
string safeArtist = string.IsNullOrWhiteSpace(artist) ? "No artist" : artist;
DateTime now = DateTime.Now;
string attribution = $"Date: {now:MM/dd/yyyy} | Time: {now:hh:mm tt}";
try
{
ToastNotificationService.ShowTrackChangeToast(safeTitle, safeArtist, attribution, outputImagePath);
}
catch (Exception ex)
{
#if DEBUG
System.Diagnostics.Debug.WriteLine($"[Toast] ShowTrackChangeToast failed: {ex}");
MessageBox.Show($"Toast display failed: {ex.Message}", "Toast Debug", MessageBoxButtons.OK, MessageBoxIcon.Warning);
#endif
}
ToastNotificationService.ShowTrackChangeToast(safeTitle, safeArtist, attribution, outputImagePath);
}
}
+4 -4
View File
@@ -193,7 +193,7 @@
//
// button3
//
this.button3.Location = new System.Drawing.Point(71, 535);
this.button3.Location = new System.Drawing.Point(62, 587);
this.button3.Name = "button3";
this.button3.Size = new System.Drawing.Size(120, 30);
this.button3.TabIndex = 8;
@@ -202,7 +202,7 @@
//
// button4
//
this.button4.Location = new System.Drawing.Point(316, 535);
this.button4.Location = new System.Drawing.Point(309, 587);
this.button4.Name = "button4";
this.button4.Size = new System.Drawing.Size(120, 30);
this.button4.TabIndex = 9;
@@ -345,7 +345,7 @@
//
this.toast_box_group_box.Controls.Add(this.toast_check_box_enable);
this.toast_box_group_box.Controls.Add(this.toast_label);
this.toast_box_group_box.Location = new System.Drawing.Point(133, 575);
this.toast_box_group_box.Location = new System.Drawing.Point(118, 534);
this.toast_box_group_box.Name = "toast_box_group_box";
this.toast_box_group_box.Size = new System.Drawing.Size(248, 42);
this.toast_box_group_box.TabIndex = 4;
@@ -517,7 +517,7 @@
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(553, 764);
this.ClientSize = new System.Drawing.Size(542, 764);
this.Controls.Add(this.groupBox2);
this.Controls.Add(this.button2);
this.Controls.Add(this.ok_button_master);