Skip to content
42 changes: 28 additions & 14 deletions WinampNowPlayingToFile/Business/NowPlayingToFileManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
using WinampNowPlayingToFile.Facade;
using WinampNowPlayingToFile.Settings;
using Song = WinampNowPlayingToFile.Facade.Song;
using System.Drawing.Text;

namespace WinampNowPlayingToFile.Business;

Expand All @@ -36,7 +37,13 @@ public class NowPlayingToFileManager: INowPlayingToFileManager {
private readonly FormatCompiler templateCompiler = new();
internal readonly Timer renderTextTimer = new(1000);

private Generator? cachedTemplate;
internal struct templateWithGenerator {
internal string fileName { get; set; }
internal Generator generator { get; set; }
}

private List<templateWithGenerator> cachedTemplates = new List<templateWithGenerator>();

private bool _textTemplateDependsOnTime;

private bool textTemplateDependsOnTime {
Expand All @@ -63,7 +70,7 @@ public NowPlayingToFileManager(ISettings settings, WinampController winampContro
};

this.settings.settingsUpdated += delegate {
cachedTemplate = null;
cachedTemplates.Clear();
textTemplateDependsOnTime = false;
update();
};
Expand All @@ -82,7 +89,9 @@ public NowPlayingToFileManager(ISettings settings, WinampController winampContro
internal void update(bool updateAlbumArt = true) {
try {
if (winampController.currentSong is { Filename: not "" } currentSong) {
saveText(renderText(currentSong));
foreach (textTemplate template in settings.textTemplates) {
saveText(template.fileName, renderText(currentSong, template));
}

if (updateAlbumArt) {
saveImage(findAlbumArt(currentSong));
Expand All @@ -93,21 +102,24 @@ internal void update(bool updateAlbumArt = true) {
}
}

internal string renderText(Song currentSong) {
return winampController.status == Status.Playing ? getTemplate().Render(currentSong) : string.Empty;
internal string renderText(Song currentSong, textTemplate template) {
return winampController.status == Status.Playing ? getTemplate(template).Render(currentSong) : string.Empty;
}

private void saveText(string nowPlayingText) {
File.WriteAllText(settings.textFilename, nowPlayingText, UTF8);
private void saveText(string fileName, string nowPlayingText) {
File.WriteAllText(fileName, nowPlayingText, UTF8);
}

private Generator getTemplate() {
if (cachedTemplate == null) {
cachedTemplate = templateCompiler.Compile(settings.textTemplate);
cachedTemplate.KeyNotFound += fetchExtraMetadata;
private Generator getTemplate(textTemplate template) {
Generator templateGenerator;
if (cachedTemplates.Exists(x => x.fileName == template.fileName)) {
templateGenerator = cachedTemplates.Single(x => x.fileName == template.fileName).generator;
} else {
templateGenerator = templateCompiler.Compile(template.text);
templateGenerator.KeyNotFound += fetchExtraMetadata;
cachedTemplates.Add(new templateWithGenerator() { fileName = template.fileName, generator = templateGenerator });
}

return cachedTemplate;
return templateGenerator;
}

private void fetchExtraMetadata(object sender, KeyNotFoundEventArgs args) {
Expand Down Expand Up @@ -211,7 +223,9 @@ private void startOrStopTextRenderingTimer(Status? playbackStatus = null) {

public virtual void onQuit() {
renderTextTimer.Stop();
saveText(string.Empty);
foreach (textTemplate template in settings.textTemplates) {
saveText(template.fileName, string.Empty);
}
saveImage(albumArtWhenStopped);
}

Expand Down
16 changes: 16 additions & 0 deletions WinampNowPlayingToFile/Facade/textTemplate.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
namespace WinampNowPlayingToFile.Facade;

public struct textTemplate {
public textTemplate(string fileName = null!, string text = null!) {
this.fileName = fileName!;
this.text = text!;
}

public string fileName { get; set; }
public string text { get; set; }

public override string ToString() {
return text;
}
}

1,335 changes: 693 additions & 642 deletions WinampNowPlayingToFile/Presentation/SettingsDialog.Designer.cs

Large diffs are not rendered by default.

99 changes: 72 additions & 27 deletions WinampNowPlayingToFile/Presentation/SettingsDialog.cs
Original file line number Diff line number Diff line change
Expand Up @@ -76,26 +76,32 @@ public SettingsDialog(ISettings settings, WinampControllerImpl winampController)
}

private void SettingsDialog_Load(object sender, EventArgs e) {
textFilenameEditor.InitialDirectory = Path.GetDirectoryName(settings.textFilename);
textFilenameEditor.FileName = settings.textFilename;
// Since the templateSelector holds all textTemplates let's not fire textBox events since those update the templateSelector too
templateEditor.TextChanged -= onTemplateChange;
textFilename.TextChanged -= onFilenameChange;
albumArtFilename.TextChanged -= onFilenameChange;

albumArtFilenameEditor.InitialDirectory = Path.GetDirectoryName(settings.albumArtFilename);
albumArtFilenameEditor.FileName = settings.albumArtFilename;
albumArtFilenameEditor.InitialDirectory = Path.GetDirectoryName(settings.albumArtFilename);
albumArtFilenameEditor.FileName = settings.albumArtFilename;
albumArtFilename.Text = settings.albumArtFilename;

textFilename.Text = settings.textFilename;
albumArtFilename.Text = settings.albumArtFilename;
foreach (textTemplate item in settings.textTemplates) {
templateSelector.Items.Add(item);
}
templateSelector.SelectedIndex = 0;

templateEditor.Text = settings.textTemplate;
templateEditor.Select(templateEditor.TextLength, 0);
templateEditor.TextChanged += onTemplateChange;
textFilename.TextChanged += onFilenameChange;
albumArtFilename.TextChanged += onFilenameChange;

winampController.songChanged += delegate { renderPreview(); };
renderTextTimer.Elapsed += delegate { renderPreview(); };
winampController.songChanged += delegate { renderPreview(templatePreview); };
renderTextTimer.Elapsed += delegate { renderPreview(templatePreview); };

applyButton.Enabled = false;
}

private void onTextFileBrowseButtonClick(object sender, EventArgs e) {
onBrowseButtonClick(textFilenameEditor, textFilename);
onBrowseButtonClick(textFilenameEditor, textFilename);
}

private void onAlbumArtBrowseButtonClick(object sender, EventArgs e) {
Expand Down Expand Up @@ -124,19 +130,20 @@ private void onCancel(object sender, EventArgs e) {
}

private void onTemplateChange(object sender, EventArgs e) {
renderPreview();
onFormDirty();
renderPreview(sender);
templateSelector.Items[templateSelector.SelectedIndex] = new textTemplate(textFilename.Text, templateEditor.Text);
onFormDirty();
}

private void renderPreview() {
private void renderPreview(object sender) {
Song previewSong = isSongPlaying() ? winampController.currentSong : EXAMPLE_SONG;

try {
templatePreview.Text = compileTemplate().Render(previewSong);
templatePreview.Text = compileTemplate().Render(previewSong);
} catch (KeyNotFoundException e) {
templatePreview.Text = $"Template key not found: {e.Message}";
templatePreview.Text = $"Template key not found: {e.Message}";
} catch (FormatException e) {
templatePreview.Text = $"Template format error: {e.Message}";
templatePreview.Text = $"Template format error: {e.Message}";
}
}

Expand All @@ -156,8 +163,9 @@ private Generator compileTemplate() {
return generator;
}


private void showTemplateMenu(object sender, EventArgs e) {
insertTemplatePlaceholderMenu.Show(templateInsertButton, new Point(0, templateInsertButton.Height));
insertTemplatePlaceholderMenu.Show(templateInsertButton, new Point(0, templateInsertButton.Height));
}

private void onTemplateMenuSelection(object sender, ToolStripItemClickedEventArgs e) {
Expand All @@ -177,7 +185,7 @@ private void onTemplateMenuSelection(object sender, ToolStripItemClickedEventArg
textToInsert = e.ClickedItem.Text;
}

string placeholder = $$$"""{{{{{textToInsert}}}}}""";
string placeholder = $$$"""{{{{{textToInsert}}}}}""";
string originalTemplate = templateEditor.Text;
int selectionStart = templateEditor.SelectionStart;
int selectionEnd = selectionStart + templateEditor.SelectionLength;
Expand All @@ -187,10 +195,10 @@ private void onTemplateMenuSelection(object sender, ToolStripItemClickedEventArg
newTemplate.Append(placeholder);
newTemplate.Append(originalTemplate.Substring(selectionEnd));

templateEditor.Text = newTemplate.ToString();
templateEditor.SelectionLength = 0;
templateEditor.SelectionStart = selectionStart + placeholder.Length;
templateEditor.Focus();
templateEditor.Text = newTemplate.ToString();
templateEditor.SelectionLength = 0;
templateEditor.SelectionStart = selectionStart + placeholder.Length;
templateEditor.Focus();
}
}

Expand All @@ -215,9 +223,10 @@ private void save() {
try {
compileTemplate().Render(EXAMPLE_SONG);

settings.textFilename = textFilename.Text;
settings.albumArtFilename = albumArtFilename.Text;
settings.textTemplate = templateEditor.Text;
// Actually the templateSelector will hold all of the templates now
settings.textTemplates = templateSelector.Items.Cast<textTemplate>().ToList();

settings.albumArtFilename = albumArtFilename.Text;
settings.save();

applyButton.Enabled = false;
Expand All @@ -236,12 +245,48 @@ private void onSubmitFilename(object sender, CancelEventArgs e) {
}

private void onFilenameChange(object sender, EventArgs e) {
onFormDirty();
templateSelector.Items[templateSelector.SelectedIndex] = new textTemplate(textFilename.Text, templateEditor.Text);
onFormDirty();
}

protected override void OnClosed(EventArgs e) {
renderTextTimer.Dispose();
base.OnClosed(e);
}

private void onTemplateSelectorChanged(object sender, EventArgs e) {
bool applyButtonState = applyButton.Enabled;

textTemplate selectedTemplate = (textTemplate)templateSelector.SelectedItem;
textFilenameEditor.InitialDirectory = Path.GetDirectoryName(selectedTemplate.fileName);
textFilenameEditor.FileName = selectedTemplate.fileName;

textFilename.Text = selectedTemplate.fileName;

templateEditor.Text = selectedTemplate.text;

applyButton.Enabled = applyButtonState; // Restore dirty state
}

private void onTemplateAdd(object sender, EventArgs e) {
textTemplate newTemplate = settings.getDefault(); // Add an index to the new template's file name
FileInfo newTemplatePath = new FileInfo(newTemplate.fileName);
string newTemplateFileName = Path.Combine(
newTemplatePath.DirectoryName,
$"{Path.GetFileNameWithoutExtension(newTemplatePath.FullName)}{(settings.textTemplates.Count+1)}{newTemplatePath.Extension}"
);
newTemplate.fileName = newTemplateFileName;
settings.textTemplates.Add(newTemplate);

templateSelector.Items.Add(settings.textTemplates.Last());
templateSelector.SelectedIndex = templateSelector.Items.Count - 1;
onFormDirty();
}

private void onTemmplateRemove(object sender, EventArgs e) {
settings.textTemplates.Remove((textTemplate)templateSelector.SelectedItem);
templateSelector.Items.Remove(templateSelector.SelectedItem);
templateSelector.SelectedIndex = templateSelector.Items.Count - 1;
onFormDirty();
}
}
3 changes: 3 additions & 0 deletions WinampNowPlayingToFile/Presentation/SettingsDialog.resx
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,9 @@
<metadata name="insertTemplatePlaceholderMenu.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>161, 17</value>
</metadata>
<data name="explanationLabel.Text" xml:space="preserve">
<value>When Winamp plays a track, this plug-in will save the track information and album art to files. The format of the text can be customized with templates. Create more text files with different templates by adding more with the plus button. Note: All templates will be saved at once upon pressing OK or Apply.</value>
</data>
<metadata name="albumArtFilenameEditor.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>374, 21</value>
</metadata>
Expand Down
22 changes: 17 additions & 5 deletions WinampNowPlayingToFile/Settings/BaseSettings.cs
Original file line number Diff line number Diff line change
@@ -1,27 +1,39 @@
#nullable enable

using System;
using System.Collections.Generic;

using TagLib.Riff;

using WinampNowPlayingToFile.Facade;

namespace WinampNowPlayingToFile.Settings;

public abstract class BaseSettings: ISettings {

public string textFilename { get; set; } = null!;
public string albumArtFilename { get; set; } = null!;
public string textTemplate { get; set; } = null!;
public List<textTemplate> textTemplates { get; set; } = null!;

public event EventHandler? settingsUpdated;
public event EventHandler? settingsUpdated;

public abstract void load();
public virtual void save() { }

public ISettings loadDefaults() {
textFilename = Environment.ExpandEnvironmentVariables(@"%TEMP%\winamp_now_playing.txt");
textTemplates = new List<textTemplate>() {
getDefault()
};
albumArtFilename = Environment.ExpandEnvironmentVariables(@"%TEMP%\winamp_now_playing.png");
textTemplate = "{{#if Artist}}{{Artist}} \u2013 {{/if}}{{Title}}{{#if Album}} \u2013 {{Album}}{{/if}}";
return this;
}

public textTemplate getDefault() {
return new textTemplate(
fileName: Environment.ExpandEnvironmentVariables(@"%TEMP%\winamp_now_playing.txt"),
text: "{{#if Artist}}{{Artist}} \u2013 {{/if}}{{Title}}{{#if Album}} \u2013 {{Album}}{{/if}}"
);
}

protected void onSettingsUpdated() {
settingsUpdated?.Invoke(this, EventArgs.Empty);
}
Expand Down
12 changes: 8 additions & 4 deletions WinampNowPlayingToFile/Settings/ISettings.cs
Original file line number Diff line number Diff line change
@@ -1,17 +1,21 @@
using System;
using System.Collections.Generic;

namespace WinampNowPlayingToFile.Settings;
using WinampNowPlayingToFile.Facade;

public interface ISettings {
namespace WinampNowPlayingToFile.Settings;

public partial interface ISettings {

string textFilename { get; set; }
string albumArtFilename { get; set; }
string textTemplate { get; set; }

abstract List<textTemplate> textTemplates { get; set; }

event EventHandler settingsUpdated;

void load();
ISettings loadDefaults();
textTemplate getDefault();
void save();

}
Loading