18 Commits
1.0.0 ... 1.9.0

Author SHA1 Message Date
ea86ff00db Update README.md 2026-04-13 00:49:27 -04:00
e73176f806 Added some endpoint
I added some endpoints but they were already in there but I wanted them documented
2026-04-13 00:46:48 -04:00
ca20267307 Added new information for web server 2026-04-13 00:42:27 -04:00
minster586
a31bcc2971 Updated version and finally got web server running 2026-04-13 00:21:18 -04:00
minster586
761b382364 Made some minor changes and set in a widget for the web 2026-04-12 23:45:04 -04:00
minster586
79268a61ff update version 2025-10-10 04:28:22 -04:00
minster586
6c6ee120ce Made a few design changes 2025-10-10 04:24:49 -04:00
minster586
d07c32556b Trying to work on the marquee 2025-10-10 03:58:19 -04:00
minster586
7c1da23da0 I have moved the load profile to the main window 2025-10-10 03:18:11 -04:00
minster586
9fecd18519 uploaded screenshots 2025-09-08 17:52:06 -04:00
minster586
f5708c6dc6 Updated the readme 2025-09-08 17:19:38 -04:00
minster586
5bc4aaaf00 ReadME 2025-09-08 16:40:23 -04:00
minster586
18ad5c1440 Added Dark mode 2025-09-08 16:38:29 -04:00
minster586
8fa429575d This should be done 2025-08-27 18:16:59 -04:00
minster586
12cdde5044 This should be complete 2025-08-27 18:16:16 -04:00
d7d60a8665 New Readme 2025-08-27 18:13:50 -04:00
0122108db9 Need to upload new file 2025-08-27 18:13:27 -04:00
minster586
f2922ba93c Set some icons 2025-08-27 17:48:36 -04:00
24 changed files with 1881 additions and 287 deletions

1
.gitignore vendored
View File

@@ -361,3 +361,4 @@ MigrationBackup/
# Fody - auto-generated XML schema
FodyWeavers.xsd
/README.md

View File

@@ -1 +1,59 @@
# RadioDJViewer
# RadioDJViewer v1.8.0
**RadioDJViewer** is a high-performance Windows application designed to bridge the gap between RadioDJ and your broadcast visuals. It fetches live "Now Playing" data from the RadioDJ REST API and serves it simultaneously to physical local files and a built-in network web server.
## 🚀 New in v1.8.0: The Web Update
* **Integrated Web Server:** Hosts a live, high-performance web widget for OBS Browser Sources.
* **Tri-State Status Indicator:**
* 🔴 **Red:** Disconnected from RadioDJ API.
* 🟢 **Green:** Connected to API (Web Server Off).
* 🔵 **Blue:** Full Broadcast Mode (API Connected + Web Server Live).
* **Memory-Caching:** Data is served to the web widget directly from RAM for zero-latency updates and reduced disk wear.
* **Network-Wide Access:** View your "Now Playing" widget on any device (Phones, Tablets, or second PCs) on your local network.
## 🛠 Features
* Connects to RadioDJ REST API to acquire real-time song info.
* Displays title, artist, and album (with scrolling marquee for long text).
* Dual-Output: Exports `.txt` and `.png` files for legacy GDI+ sources while hosting the web stream.
* Supports multiple profiles for different RadioDJ instances.
* Auto-regenerates missing `profiles.json` and fallback images.
## 🌐 Web API Endpoints
Once the server is active (Blue Status), you can access the following data URLs locally or over your network:
| Resource | URL (Localhost) | Purpose |
| :--- | :--- | :--- |
| **Main Widget** | `http://127.0.0.1:8181/` | Loads the full HTML/CSS/JS widget for OBS. |
| **Song Title** | `http://127.0.0.1:8181/title.txt` | Returns the raw text of the current song. |
| **Artist Name** | `http://127.0.0.1:8181/artist.txt` | Returns the raw text of the current artist. |
| **Album Art** | `http://127.0.0.1:8181/Album-Art.png` | Serves the current album art image bytes. |
*Note: Replace `127.0.0.1` with your computer's local IP address to access these from other devices.*
## 📋 Requirements
* Windows 7 or later.
* .NET Framework 4.7.2.
* RadioDJ with the **REST Server Plugin** enabled and configured.
## 🔧 Installation & Setup
1. **Download & Extract:** Run the `RadioDJViewer.exe`.
2. **Run as Administrator:** This is required for the Web Server to bind to your network port.
3. **Configure API:** Enter your RadioDJ IP, Port, and Auth Key in the settings.
4. **Set Output:** Choose a folder for your physical text and image files (e.g., `H:/RDJ-Output/`).
5. **Connect:** Click **Connect**. Your status box should turn **Blue** when everything is active.
## 🌐 Network & Firewall Setup
To allow other devices to see the widget, you must open the port in the Windows Firewall:
1. Open **Windows Firewall with Advanced Security**.
2. Create a **New Inbound Rule** -> **Port** -> **TCP**.
3. Enter Port **8181** (or your chosen port) and select **Allow the connection**.
## 📝 Credits
This program was developed as a learning journey into C# and .NET, with collaborative assistance from **Microsoft Copilot** and **Google Gemini** to refine the API parsing and web server logic.
## 🔗 Links
* **Repository:** [https://git.smartcraft.me/minster586/RadioDJViewer](https://git.smartcraft.me/minster586/RadioDJViewer)
* **Contact/Stream:** [https://links.smartcraft.me/@minster586](https://links.smartcraft.me/@minster586)
---
For questions or issues, please open an issue on the repository or contact the maintainer.

View File

@@ -0,0 +1,41 @@
using System;
using System.IO;
using System.Linq;
using System.Reflection;
namespace RadioDJViewer
{
public static class EmbeddedResourceHelper
{
/// <summary>
/// Reads an embedded resource's text content by matching the resource name ending with the provided file name.
/// Example: call Read("index.html") to load a resource named "RadioDJViewer.Web.index.html" or similar.
/// </summary>
public static string Read(string fileName)
{
if (string.IsNullOrEmpty(fileName))
throw new ArgumentNullException(nameof(fileName));
var asm = Assembly.GetExecutingAssembly();
var resourceName = asm.GetManifestResourceNames()
.FirstOrDefault(n => n.EndsWith(fileName, StringComparison.OrdinalIgnoreCase));
if (resourceName == null)
{
var names = string.Join(", ", asm.GetManifestResourceNames());
throw new FileNotFoundException($"Embedded resource '{fileName}' not found. Available resources: {names}");
}
using (var stream = asm.GetManifestResourceStream(resourceName))
{
if (stream == null)
throw new FileNotFoundException($"Resource stream '{resourceName}' is null.");
using (var reader = new StreamReader(stream))
{
return reader.ReadToEnd();
}
}
}
}
}

View File

@@ -37,9 +37,11 @@
this.menuStrip2 = new System.Windows.Forms.MenuStrip();
this.fileToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.restAPISettingToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.loggingToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.onToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.offToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.exitToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.aboutToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.pictureBox1 = new System.Windows.Forms.PictureBox();
this.groupBox1 = new System.Windows.Forms.GroupBox();
this.label6 = new System.Windows.Forms.Label();
this.label5 = new System.Windows.Forms.Label();
@@ -49,10 +51,17 @@
this.label1 = new System.Windows.Forms.Label();
this.button1 = new System.Windows.Forms.Button();
this.button2 = new System.Windows.Forms.Button();
this.uicolor = new System.Windows.Forms.Button();
this.pictureBox1 = new System.Windows.Forms.PictureBox();
this.profile_drop_box = new System.Windows.Forms.ComboBox();
this.profile_label = new System.Windows.Forms.Label();
this.load_profile_button = new System.Windows.Forms.Button();
this.label7 = new System.Windows.Forms.Label();
this.label8 = new System.Windows.Forms.Label();
this.statusStrip1.SuspendLayout();
this.menuStrip2.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit();
this.groupBox1.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit();
this.SuspendLayout();
//
// statusStrip1
@@ -62,7 +71,7 @@
this.toolStripStatusLabel2,
this.toolStripStatusLabel4,
this.toolStripStatusLabel3});
this.statusStrip1.Location = new System.Drawing.Point(0, 268);
this.statusStrip1.Location = new System.Drawing.Point(0, 314);
this.statusStrip1.Name = "statusStrip1";
this.statusStrip1.Size = new System.Drawing.Size(612, 25);
this.statusStrip1.TabIndex = 0;
@@ -77,9 +86,12 @@
//
// toolStripStatusLabel2
//
this.toolStripStatusLabel2.Image = ((System.Drawing.Image)(resources.GetObject("toolStripStatusLabel2.Image")));
this.toolStripStatusLabel2.Name = "toolStripStatusLabel2";
this.toolStripStatusLabel2.Size = new System.Drawing.Size(16, 20);
// Use BackColor only to indicate status. Text is a single space so the label renders as a square.
this.toolStripStatusLabel2.Text = " ";
this.toolStripStatusLabel2.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text;
this.toolStripStatusLabel2.BackColor = System.Drawing.Color.Red;
//
// toolStripStatusLabel4
//
@@ -113,6 +125,7 @@
//
this.fileToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.restAPISettingToolStripMenuItem,
this.loggingToolStripMenuItem,
this.exitToolStripMenuItem});
this.fileToolStripMenuItem.Name = "fileToolStripMenuItem";
this.fileToolStripMenuItem.Size = new System.Drawing.Size(37, 20);
@@ -121,14 +134,36 @@
// restAPISettingToolStripMenuItem
//
this.restAPISettingToolStripMenuItem.Name = "restAPISettingToolStripMenuItem";
this.restAPISettingToolStripMenuItem.Size = new System.Drawing.Size(180, 22);
this.restAPISettingToolStripMenuItem.Size = new System.Drawing.Size(118, 22);
this.restAPISettingToolStripMenuItem.Text = "Setting";
this.restAPISettingToolStripMenuItem.Click += new System.EventHandler(this.restAPISettingToolStripMenuItem_Click);
//
// loggingToolStripMenuItem
//
this.loggingToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.onToolStripMenuItem,
this.offToolStripMenuItem});
this.loggingToolStripMenuItem.Enabled = false;
this.loggingToolStripMenuItem.Name = "loggingToolStripMenuItem";
this.loggingToolStripMenuItem.Size = new System.Drawing.Size(118, 22);
this.loggingToolStripMenuItem.Text = "Logging";
//
// onToolStripMenuItem
//
this.onToolStripMenuItem.Name = "onToolStripMenuItem";
this.onToolStripMenuItem.Size = new System.Drawing.Size(91, 22);
this.onToolStripMenuItem.Text = "On";
//
// offToolStripMenuItem
//
this.offToolStripMenuItem.Name = "offToolStripMenuItem";
this.offToolStripMenuItem.Size = new System.Drawing.Size(91, 22);
this.offToolStripMenuItem.Text = "Off";
//
// exitToolStripMenuItem
//
this.exitToolStripMenuItem.Name = "exitToolStripMenuItem";
this.exitToolStripMenuItem.Size = new System.Drawing.Size(157, 22);
this.exitToolStripMenuItem.Size = new System.Drawing.Size(118, 22);
this.exitToolStripMenuItem.Text = "Exit";
this.exitToolStripMenuItem.Click += new System.EventHandler(this.exitToolStripMenuItem_Click);
//
@@ -139,16 +174,10 @@
this.aboutToolStripMenuItem.Text = "About";
this.aboutToolStripMenuItem.Click += new System.EventHandler(this.aboutToolStripMenuItem_Click);
//
// pictureBox1
//
this.pictureBox1.Location = new System.Drawing.Point(13, 37);
this.pictureBox1.Name = "pictureBox1";
this.pictureBox1.Size = new System.Drawing.Size(180, 173);
this.pictureBox1.TabIndex = 3;
this.pictureBox1.TabStop = false;
//
// groupBox1
//
this.groupBox1.Controls.Add(this.label8);
this.groupBox1.Controls.Add(this.label7);
this.groupBox1.Controls.Add(this.label6);
this.groupBox1.Controls.Add(this.label5);
this.groupBox1.Controls.Add(this.label4);
@@ -165,64 +194,67 @@
//
// label6
//
this.label6.AutoSize = true;
this.label6.Location = new System.Drawing.Point(170, 130);
this.label6.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.label6.Location = new System.Drawing.Point(99, 140);
this.label6.Name = "label6";
this.label6.Size = new System.Drawing.Size(65, 16);
this.label6.Size = new System.Drawing.Size(266, 17);
this.label6.TabIndex = 5;
this.label6.Text = "No album";
this.label6.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
//
// label5
//
this.label5.AutoSize = true;
this.label5.Location = new System.Drawing.Point(170, 83);
this.label5.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.label5.Location = new System.Drawing.Point(99, 84);
this.label5.Name = "label5";
this.label5.Size = new System.Drawing.Size(56, 16);
this.label5.Size = new System.Drawing.Size(266, 17);
this.label5.TabIndex = 4;
this.label5.Text = "No artist";
this.label5.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
//
// label4
//
this.label4.AutoSize = true;
this.label4.Location = new System.Drawing.Point(170, 39);
this.label4.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.label4.Location = new System.Drawing.Point(99, 33);
this.label4.Name = "label4";
this.label4.Size = new System.Drawing.Size(48, 16);
this.label4.Size = new System.Drawing.Size(266, 16);
this.label4.TabIndex = 3;
this.label4.Text = "No title";
this.label4.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
//
// label3
//
this.label3.AutoSize = true;
this.label3.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, ((System.Drawing.FontStyle)((System.Drawing.FontStyle.Bold | System.Drawing.FontStyle.Underline))), System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.label3.Location = new System.Drawing.Point(23, 126);
this.label3.Font = new System.Drawing.Font("Microsoft Sans Serif", 14.25F, ((System.Drawing.FontStyle)((System.Drawing.FontStyle.Bold | System.Drawing.FontStyle.Underline))), System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.label3.Location = new System.Drawing.Point(23, 138);
this.label3.Name = "label3";
this.label3.Size = new System.Drawing.Size(64, 20);
this.label3.Size = new System.Drawing.Size(76, 24);
this.label3.TabIndex = 2;
this.label3.Text = "Album:";
//
// label2
//
this.label2.AutoSize = true;
this.label2.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, ((System.Drawing.FontStyle)((System.Drawing.FontStyle.Bold | System.Drawing.FontStyle.Underline))), System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.label2.Location = new System.Drawing.Point(23, 80);
this.label2.Font = new System.Drawing.Font("Microsoft Sans Serif", 14.25F, ((System.Drawing.FontStyle)((System.Drawing.FontStyle.Bold | System.Drawing.FontStyle.Underline))), System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.label2.Location = new System.Drawing.Point(23, 82);
this.label2.Name = "label2";
this.label2.Size = new System.Drawing.Size(57, 20);
this.label2.Size = new System.Drawing.Size(62, 24);
this.label2.TabIndex = 1;
this.label2.Text = "Artist:";
//
// label1
//
this.label1.AutoSize = true;
this.label1.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, ((System.Drawing.FontStyle)((System.Drawing.FontStyle.Bold | System.Drawing.FontStyle.Underline))), System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.label1.Location = new System.Drawing.Point(23, 35);
this.label1.Font = new System.Drawing.Font("Microsoft Sans Serif", 14.25F, ((System.Drawing.FontStyle)((System.Drawing.FontStyle.Bold | System.Drawing.FontStyle.Underline))), System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.label1.Location = new System.Drawing.Point(23, 29);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(48, 20);
this.label1.Size = new System.Drawing.Size(56, 24);
this.label1.TabIndex = 0;
this.label1.Text = "Title:";
//
// button1
//
this.button1.Location = new System.Drawing.Point(250, 216);
this.button1.Location = new System.Drawing.Point(62, 230);
this.button1.Name = "button1";
this.button1.Size = new System.Drawing.Size(99, 42);
this.button1.TabIndex = 5;
@@ -232,7 +264,7 @@
//
// button2
//
this.button2.Location = new System.Drawing.Point(392, 216);
this.button2.Location = new System.Drawing.Point(180, 230);
this.button2.Name = "button2";
this.button2.Size = new System.Drawing.Size(99, 42);
this.button2.TabIndex = 6;
@@ -240,26 +272,94 @@
this.button2.UseVisualStyleBackColor = true;
this.button2.Click += new System.EventHandler(this.button2_Click);
//
// uicolor
//
this.uicolor.Image = global::RadioDJViewer.Properties.Resources.MdiWeatherNight;
this.uicolor.Location = new System.Drawing.Point(572, 276);
this.uicolor.Name = "uicolor";
this.uicolor.Size = new System.Drawing.Size(28, 23);
this.uicolor.TabIndex = 7;
this.uicolor.UseVisualStyleBackColor = true;
//
// pictureBox1
//
this.pictureBox1.Location = new System.Drawing.Point(13, 37);
this.pictureBox1.Name = "pictureBox1";
this.pictureBox1.Size = new System.Drawing.Size(180, 173);
this.pictureBox1.TabIndex = 3;
this.pictureBox1.TabStop = false;
//
// profile_drop_box
//
this.profile_drop_box.FormattingEnabled = true;
this.profile_drop_box.Location = new System.Drawing.Point(62, 278);
this.profile_drop_box.Name = "profile_drop_box";
this.profile_drop_box.Size = new System.Drawing.Size(217, 21);
this.profile_drop_box.TabIndex = 8;
//
// profile_label
//
this.profile_label.AutoSize = true;
this.profile_label.Location = new System.Drawing.Point(12, 281);
this.profile_label.Name = "profile_label";
this.profile_label.Size = new System.Drawing.Size(44, 13);
this.profile_label.TabIndex = 9;
this.profile_label.Text = "Profiles:";
this.profile_label.Click += new System.EventHandler(this.label7_Click);
//
// load_profile_button
//
this.load_profile_button.Location = new System.Drawing.Point(285, 276);
this.load_profile_button.Name = "load_profile_button";
this.load_profile_button.Size = new System.Drawing.Size(75, 23);
this.load_profile_button.TabIndex = 10;
this.load_profile_button.Text = "Load Profile";
this.load_profile_button.UseVisualStyleBackColor = true;
//
// label7
//
this.label7.AutoSize = true;
this.label7.Location = new System.Drawing.Point(19, 61);
this.label7.Name = "label7";
this.label7.Size = new System.Drawing.Size(357, 16);
this.label7.TabIndex = 6;
this.label7.Text = "――――――――――――――――――――――――――――――――――――――――――――――――――";
//
// label8
//
this.label8.AutoSize = true;
this.label8.Location = new System.Drawing.Point(24, 118);
this.label8.Name = "label8";
this.label8.Size = new System.Drawing.Size(357, 16);
this.label8.TabIndex = 7;
this.label8.Text = "――――――――――――――――――――――――――――――――――――――――――――――――――";
this.label8.TextAlign = System.Drawing.ContentAlignment.BottomCenter;
//
// Main
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(612, 293);
this.ClientSize = new System.Drawing.Size(612, 339);
this.Controls.Add(this.load_profile_button);
this.Controls.Add(this.profile_label);
this.Controls.Add(this.profile_drop_box);
this.Controls.Add(this.uicolor);
this.Controls.Add(this.button2);
this.Controls.Add(this.button1);
this.Controls.Add(this.groupBox1);
this.Controls.Add(this.pictureBox1);
this.Controls.Add(this.statusStrip1);
this.Controls.Add(this.menuStrip2);
this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
this.Name = "Main";
this.Text = "RadioDJViewer - OBS Text GDI";
this.statusStrip1.ResumeLayout(false);
this.statusStrip1.PerformLayout();
this.menuStrip2.ResumeLayout(false);
this.menuStrip2.PerformLayout();
((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).EndInit();
this.groupBox1.ResumeLayout(false);
this.groupBox1.PerformLayout();
((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).EndInit();
this.ResumeLayout(false);
this.PerformLayout();
@@ -287,6 +387,15 @@
private System.Windows.Forms.Label label1;
private System.Windows.Forms.Button button1;
private System.Windows.Forms.Button button2;
private System.Windows.Forms.ToolStripMenuItem loggingToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem onToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem offToolStripMenuItem;
private System.Windows.Forms.Button uicolor;
private System.Windows.Forms.ComboBox profile_drop_box;
private System.Windows.Forms.Label profile_label;
private System.Windows.Forms.Button load_profile_button;
private System.Windows.Forms.Label label7;
private System.Windows.Forms.Label label8;
}
}

View File

@@ -8,6 +8,7 @@ using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Xml;
using System.Threading;
using System.IO;
using System.Resources;
@@ -15,6 +16,7 @@ namespace RadioDJViewer
{
public partial class Main : Form
{
private string _currentTrackKey = string.Empty;
private string selectedFolderPath = string.Empty;
private string outputFolderPath = string.Empty;
private bool isConnected = false;
@@ -23,32 +25,63 @@ namespace RadioDJViewer
private string defaultImagePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "default.jpg");
private string mainImagesFolderPath = string.Empty; // New field for main images folder path
private Profile loadedProfile = null; // Field to store the loaded profile
private WidgetServer widgetServer = null; // Local widget HTTP server
private bool widgetRunning = false;
private CancellationTokenSource statusCts = null;
// Timer to poll REST API
private System.Windows.Forms.Timer apiTimer;
private string lastApiXml = null;
private System.Windows.Forms.Timer marqueeTimerTitle;
private System.Windows.Forms.Timer marqueeTimerArtist;
private System.Windows.Forms.Timer marqueeTimerAlbum;
private int marqueeOffsetTitle = 0;
private int marqueeOffsetArtist = 0;
private int marqueeOffsetAlbum = 0;
private string marqueeTextTitle = "";
private string marqueeTextArtist = "";
private string marqueeTextAlbum = "";
private System.Windows.Forms.Timer pauseTimerTitle;
private System.Windows.Forms.Timer pauseTimerArtist;
private System.Windows.Forms.Timer pauseTimerAlbum;
private int marqueeScrollSpeed = 100; // Default to Medium
private int marqueePauseTime = 6000; // Default to 6 seconds
private string marqueeSeparator = " | "; // Default separator
private bool loggingEnabled = false;
private string logFilePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "RadioDJViewer.log");
private bool isDarkMode = false; // New field for dark mode
public Main()
{
InitializeComponent();
EnsureDefaultImageExists();
UpdateStatusBar();
// Auto-load profiles if profiles.json exists
AutoLoadProfiles();
// Set up timer for polling API every 3 seconds
apiTimer = new System.Windows.Forms.Timer();
apiTimer.Interval = 3000; // 3 seconds
apiTimer.Tick += ApiTimer_Tick;
// Set up marquee timer for title
marqueeTimer = new System.Windows.Forms.Timer();
marqueeTimer.Interval = 100; // Adjust speed as needed
marqueeTimer.Tick += MarqueeTimer_Tick;
// Marquee timers for each label
marqueeTimerTitle = new System.Windows.Forms.Timer();
marqueeTimerTitle.Interval = marqueeScrollSpeed;
marqueeTimerTitle.Tick += MarqueeTimerTitle_Tick;
marqueeTimerArtist = new System.Windows.Forms.Timer();
marqueeTimerArtist.Interval = marqueeScrollSpeed;
marqueeTimerArtist.Tick += MarqueeTimerArtist_Tick;
marqueeTimerAlbum = new System.Windows.Forms.Timer();
marqueeTimerAlbum.Interval = marqueeScrollSpeed;
marqueeTimerAlbum.Tick += MarqueeTimerAlbum_Tick;
// Wire up dark mode button
this.uicolor.Click += Uicolor_Click;
this.load_profile_button.Click += load_profile_button_Click;
PopulateProfileDropBox();
UpdateTheme();
// Auto-load profiles if profiles.json exists
AutoLoadProfiles();
}
private System.Windows.Forms.Timer marqueeTimer;
private int marqueeOffset = 0;
private string marqueeText = "";
private void EnsureDefaultImageExists()
{
if (!File.Exists(defaultImagePath))
@@ -62,22 +95,155 @@ namespace RadioDJViewer
}
}
private void MarqueeTimer_Tick(object sender, EventArgs e)
public void SetMarqueeScrollSpeed(string speed)
{
if (marqueeText.Length > 0)
switch (speed)
{
int visibleChars = 30; // Adjust for label width
if (marqueeText.Length > visibleChars)
case "Very Slow": marqueeScrollSpeed = 300; break;
case "Slow": marqueeScrollSpeed = 200; break;
case "Medium": marqueeScrollSpeed = 100; break;
case "Fast": marqueeScrollSpeed = 60; break;
case "Very Fast": marqueeScrollSpeed = 30; break;
default: marqueeScrollSpeed = 100; break;
}
marqueeTimerTitle.Interval = marqueeScrollSpeed;
marqueeTimerArtist.Interval = marqueeScrollSpeed;
marqueeTimerAlbum.Interval = marqueeScrollSpeed;
}
public void SetMarqueePauseTime(int seconds)
{
marqueeOffset = (marqueeOffset + 1) % marqueeText.Length;
string display = marqueeText.Substring(marqueeOffset) + " " + marqueeText.Substring(0, marqueeOffset);
label4.Text = display.Substring(0, Math.Min(visibleChars, display.Length));
marqueePauseTime = seconds * 1000;
}
public void SetMarqueeSeparator(string separator)
{
marqueeSeparator = separator;
}
private void LogMessage(string message)
{
if (!loggingEnabled) return;
try
{
File.AppendAllText(logFilePath, $"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] {message}\r\n");
}
catch { }
}
private int GetVisibleChars(Label label)
{
// 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 void SetLabelTextFull(Label label, string text)
{
// Always set the full text, and let marquee logic handle scrolling
label.Text = text;
}
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
{
label4.Text = marqueeText;
SetLabelTextFull(label4, marqueeTextTitle);
}
}
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);
}
}
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);
}
}
private void ApiTimer_Tick(object sender, EventArgs e)
@@ -113,6 +279,17 @@ namespace RadioDJViewer
{
lastApiXml = xml;
ParseAndDisplaySongInfo(xml);
// Build a key from Artist and Title and only show change if different
try
{
var newKey = $"{marqueeTextArtist}|{marqueeTextTitle}";
if (!string.Equals(newKey, _currentTrackKey, StringComparison.Ordinal))
{
_currentTrackKey = newKey;
ShowTemporaryStatus("Song Change Detected", 2000);
}
}
catch { }
}
}
}
@@ -139,7 +316,10 @@ namespace RadioDJViewer
private async void button1_Click(object sender, EventArgs e)
{
// Connect to REST API
// Show connecting message
ShowTemporaryStatus("Connecting to API...", 0);
// Connect to REST API (ConnectToRestApi will start web server if needed)
await ConnectToRestApi();
if (isConnected)
{
@@ -152,6 +332,15 @@ namespace RadioDJViewer
// Disconnect
isConnected = false;
apiTimer.Stop();
// Stop widget server when disconnecting
try
{
widgetServer?.Stop();
widgetServer = null;
widgetRunning = false;
}
catch { }
ClearStatusMessage();
UpdateStatusBar();
}
@@ -166,8 +355,6 @@ namespace RadioDJViewer
return;
}
string url = loadedProfile.UrlFormat;
// Debug: Show the URL before and after replacement
string debugBefore = url;
if (!string.IsNullOrWhiteSpace(url))
{
url = url.Replace("{ip}", loadedProfile.IP ?? "")
@@ -181,8 +368,6 @@ namespace RadioDJViewer
UpdateStatusBar();
return;
}
string debugAfter = url;
MessageBox.Show($"Connecting to: {debugAfter}\n(Original: {debugBefore})", "Debug", MessageBoxButtons.OK, MessageBoxIcon.Information);
try
{
var client = new System.Net.Http.HttpClient();
@@ -191,7 +376,17 @@ namespace RadioDJViewer
{
var xml = await response.Content.ReadAsStringAsync();
ParseAndDisplaySongInfo(xml);
try
{
_currentTrackKey = $"{marqueeTextArtist}|{marqueeTextTitle}";
}
catch { }
isConnected = true;
// Clear the 'Connecting...' persistent message and show connected
ClearStatusMessage();
UpdateStatusBar();
// Start web server immediately after API connection if profile requests it
StartWidgetServerIfEnabled();
}
else
{
@@ -280,6 +475,23 @@ namespace RadioDJViewer
}
}
private void StartMarqueeTimers()
{
marqueeOffsetTitle = 0;
marqueeOffsetArtist = 0;
marqueeOffsetAlbum = 0;
marqueeTimerTitle.Start();
marqueeTimerArtist.Start();
marqueeTimerAlbum.Start();
}
private void StopMarqueeTimers()
{
marqueeTimerTitle.Stop();
marqueeTimerArtist.Stop();
marqueeTimerAlbum.Stop();
}
private void ParseAndDisplaySongInfo(string xml)
{
try
@@ -292,20 +504,62 @@ namespace RadioDJViewer
string album = doc.SelectSingleNode("//*[translate(local-name(), 'ALBUM', 'album')='album']")?.InnerText;
string albumArt = doc.SelectSingleNode("//*[translate(local-name(), 'ALBUMART', 'albumart')='albumart']")?.InnerText;
marqueeText = !string.IsNullOrWhiteSpace(title) ? title : "No title";
marqueeOffset = 0;
marqueeTimer.Start();
label5.Text = !string.IsNullOrWhiteSpace(artist) ? artist : "No artist";
label6.Text = !string.IsNullOrWhiteSpace(album) ? album : "No album";
marqueeTextTitle = !string.IsNullOrWhiteSpace(title) ? title : "No title";
marqueeTextArtist = !string.IsNullOrWhiteSpace(artist) ? artist : "No artist";
marqueeTextAlbum = !string.IsNullOrWhiteSpace(album) ? album : "No album";
StartMarqueeTimers();
// Write to output files
if (!string.IsNullOrEmpty(outputFolderPath))
{
File.WriteAllText(Path.Combine(outputFolderPath, "title.txt"), marqueeText);
File.WriteAllText(Path.Combine(outputFolderPath, "artist.txt"), label5.Text);
File.WriteAllText(Path.Combine(outputFolderPath, "album.txt"), label6.Text);
File.WriteAllText(Path.Combine(outputFolderPath, "title.txt"), marqueeTextTitle);
File.WriteAllText(Path.Combine(outputFolderPath, "artist.txt"), marqueeTextArtist);
File.WriteAllText(Path.Combine(outputFolderPath, "album.txt"), marqueeTextAlbum);
}
// Update in-memory cache for widget server
try
{
WidgetServer.CachedArtist = marqueeTextArtist ?? string.Empty;
WidgetServer.CachedTitle = marqueeTextTitle ?? string.Empty;
byte[] imgBytes = null;
// Prefer album art from main images folder
if (!string.IsNullOrEmpty(albumArt) && !string.IsNullOrEmpty(mainImagesFolderPath))
{
var imagePath = Path.Combine(mainImagesFolderPath, albumArt);
if (File.Exists(imagePath))
{
try
{
using (var img = System.Drawing.Image.FromFile(imagePath))
{
using (var ms = new MemoryStream())
{
img.Save(ms, System.Drawing.Imaging.ImageFormat.Png);
imgBytes = ms.ToArray();
}
}
}
catch { imgBytes = null; }
}
}
// Fallback to embedded resource image
if (imgBytes == null)
{
var fallback = Properties.Resources.fallback;
if (fallback != null)
{
using (var ms = new MemoryStream())
{
fallback.Save(ms, System.Drawing.Imaging.ImageFormat.Png);
imgBytes = ms.ToArray();
}
}
}
WidgetServer.CachedImageBytes = imgBytes;
}
catch { }
// Handle album art image
if (!string.IsNullOrWhiteSpace(albumArt))
{
@@ -318,35 +572,131 @@ namespace RadioDJViewer
}
catch
{
marqueeText = "No title";
marqueeOffset = 0;
marqueeTimer.Start();
label5.Text = "No artist";
label6.Text = "No album";
marqueeTextTitle = "No title";
marqueeTextArtist = "No artist";
marqueeTextAlbum = "No album";
StartMarqueeTimers();
pictureBox1.Image = null;
}
}
private void UpdateStatusBar()
{
// Status icon
toolStripStatusLabel2.Image = isConnected ? Properties.Resources.green : Properties.Resources.red;
// Update color box based on connection states
try
{
if (!isConnected)
{
toolStripStatusLabel2.BackColor = Color.Red; // disconnected/error
}
else
{
if (isConnected && widgetRunning)
toolStripStatusLabel2.BackColor = Color.Blue; // both running
else
toolStripStatusLabel2.BackColor = Color.Green; // API ok, web off
}
}
catch { }
// Profile
toolStripStatusLabel4.Text = $"Profile: {currentProfile}";
// Song update notification (example)
// If there's no active temporary message, ensure label3 shows default
if (statusCts == null)
{
toolStripStatusLabel3.Text = isConnected ? "Connected to API" : "Disconnected";
}
}
private void ShowTemporaryStatus(string message, int milliseconds)
{
// Cancel any previous temporary status
try
{
statusCts?.Cancel();
}
catch { }
statusCts = null;
if (milliseconds <= 0)
{
// Persistent message until changed - keep a non-null token so UpdateStatusBar doesn't overwrite
var persistent = new CancellationTokenSource();
statusCts = persistent;
toolStripStatusLabel3.Text = message;
return;
}
var cts = new CancellationTokenSource();
statusCts = cts;
toolStripStatusLabel3.Text = message;
Task.Run(async () =>
{
try
{
await Task.Delay(milliseconds, cts.Token);
if (!cts.IsCancellationRequested)
{
// clear temporary message
statusCts = null;
this.BeginInvoke((Action)(() => {
toolStripStatusLabel3.Text = isConnected ? "Connected to API" : "Disconnected";
}));
}
}
catch { }
});
}
private void ClearStatusMessage()
{
try { statusCts?.Cancel(); } catch { }
statusCts = null;
try { toolStripStatusLabel3.Text = isConnected ? "Connected to API" : "Disconnected"; } catch { }
}
private void StartWidgetServerIfEnabled()
{
// Called after API connection is confirmed
if (loadedProfile == null || !loadedProfile.WebServerEnabled)
return;
try
{
if (widgetServer == null)
{
int port = loadedProfile.WebServerPort > 0 ? loadedProfile.WebServerPort : 8080;
widgetServer = new WidgetServer(port, () => marqueeTextArtist, () => marqueeTextTitle, () => this.currentSongImagePath ?? this.defaultImagePath);
}
widgetServer.Start();
widgetRunning = true;
UpdateStatusBar();
ShowTemporaryStatus($"Web: Running on {loadedProfile.WebServerPort}", 5000);
}
catch (Exception ex)
{
widgetRunning = false;
UpdateStatusBar();
// Show error message
ShowTemporaryStatus($"Web server error: {ex.Message}", 5000);
}
}
private void restAPISettingToolStripMenuItem_Click(object sender, EventArgs e)
{
// Open settings/profile dialog
var settingsForm = new Form1();
settingsForm.ApplyTheme(isDarkMode); // Pass current color mode
settingsForm.ShowDialog();
}
private void aboutToolStripMenuItem_Click(object sender, EventArgs e)
{
string version = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString();
var assembly = System.Reflection.Assembly.GetExecutingAssembly();
var versionAttr = assembly.GetCustomAttributes(typeof(System.Reflection.AssemblyFileVersionAttribute), false)
.OfType<System.Reflection.AssemblyFileVersionAttribute>().FirstOrDefault();
string version = versionAttr != null ? versionAttr.Version : assembly.GetName().Version.ToString();
string message = $"RadioDJViewer\nVersion: {version}\nGitHub: https://git.smartcraft.me/minster586/RadioDJViewer";
MessageBox.Show(message, "About RadioDJViewer", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
@@ -369,8 +719,118 @@ namespace RadioDJViewer
outputFolderPath = profile.OutputFolder;
mainImagesFolderPath = profile.MainImagesFolder; // Update new field
loadedProfile = profile; // Store loaded profile for REST API connection
// Update polling rate when profile is loaded
int pollingRate = profile.PollingRateSeconds > 0 ? profile.PollingRateSeconds : 3;
apiTimer.Interval = pollingRate * 1000;
SetMarqueeScrollSpeed(profile.MarqueeScrollSpeed ?? "Medium");
SetMarqueePauseTime(profile.MarqueePauseTime > 0 ? profile.MarqueePauseTime : 6);
SetMarqueeSeparator(profile.MarqueeSeparator ?? " | ");
// Immediately apply marquee settings to timers
if (pauseTimerTitle != null) pauseTimerTitle.Interval = marqueePauseTime;
if (pauseTimerArtist != null) pauseTimerArtist.Interval = marqueePauseTime;
if (pauseTimerAlbum != null) pauseTimerAlbum.Interval = marqueePauseTime;
marqueeTimerTitle.Interval = marqueeScrollSpeed;
marqueeTimerArtist.Interval = marqueeScrollSpeed;
marqueeTimerAlbum.Interval = marqueeScrollSpeed;
UpdateStatusBar();
// You can add more logic to update other fields if needed
}
private void Uicolor_Click(object sender, EventArgs e)
{
isDarkMode = !isDarkMode;
UpdateTheme();
}
private Bitmap InvertIcon(Bitmap original)
{
Bitmap inverted = new Bitmap(original.Width, original.Height);
for (int y = 0; y < original.Height; y++)
{
for (int x = 0; x < original.Width; x++)
{
Color pixel = original.GetPixel(x, y);
Color inv = Color.FromArgb(pixel.A, 255 - pixel.R, 255 - pixel.G, 255 - pixel.B);
inverted.SetPixel(x, y, inv);
}
}
return inverted;
}
private void UpdateTheme()
{
// Set icon
Bitmap icon = isDarkMode ? Properties.Resources.MdiWeatherSunny : Properties.Resources.MdiWeatherNight;
// Invert sun icon for visibility in dark mode
if (isDarkMode && icon != null)
{
icon = InvertIcon(icon);
}
// Resize icon to fit button and center it
if (icon != null)
{
int btnW = uicolor.Width;
int btnH = uicolor.Height;
var resizedIcon = new Bitmap(icon, btnW - 8, btnH - 8); // Padding for centering
uicolor.Image = resizedIcon;
uicolor.ImageAlign = ContentAlignment.MiddleCenter;
}
// Set colors
Color backColor = isDarkMode ? Color.FromArgb(32, 32, 32) : SystemColors.Control;
Color foreColor = isDarkMode ? Color.White : SystemColors.ControlText;
this.BackColor = backColor;
this.ForeColor = foreColor;
foreach (Control c in this.Controls)
{
ApplyThemeRecursive(c, backColor, foreColor);
}
}
private void ApplyThemeRecursive(Control control, Color backColor, Color foreColor)
{
control.BackColor = backColor;
control.ForeColor = foreColor;
foreach (Control child in control.Controls)
{
ApplyThemeRecursive(child, backColor, foreColor);
}
}
private void label7_Click(object sender, EventArgs e)
{
}
public string GetCurrentProfileName()
{
return currentProfile;
}
private void PopulateProfileDropBox()
{
profile_drop_box.Items.Clear();
var profileNames = ProfileStorage.GetProfileNames();
profile_drop_box.Items.AddRange(profileNames.ToArray());
// Set selected item to current profile if available
string currentProfile = GetCurrentProfileName();
if (!string.IsNullOrEmpty(currentProfile) && profileNames.Contains(currentProfile))
profile_drop_box.SelectedItem = currentProfile;
else if (profile_drop_box.Items.Count > 0)
profile_drop_box.SelectedIndex = 0;
}
private void load_profile_button_Click(object sender, EventArgs e)
{
if (profile_drop_box.SelectedItem != null)
{
var profile = ProfileStorage.LoadProfile(profile_drop_box.SelectedItem.ToString());
if (profile != null)
{
LoadProfile(profile);
UpdateStatusBar();
MessageBox.Show($"Profile '{profile.Name}' loaded.", "Profile Loaded", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
}
}
}
}

View File

@@ -131,4 +131,62 @@
<metadata name="menuStrip2.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>247, 17</value>
</metadata>
<data name="$this.Icon" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
AAABAAEAIBgAAAEAIACIDAAAFgAAACgAAAAgAAAAMAAAAAEAIAAAAAAAAAwAAMMOAADDDgAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAsoI9ALKFPgTFiD2EuXczxqBeJ8SUUSHCZTYYwicSC8AkDwpshUMaIYZH
FhA/GAAAYzMEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC4h0cA8pRDAMmNRDrCgDl6qWImkpxVItZqNxuLORkLnHE5
GZ2XUyPbm1gjw5ZXJFyASyAIi1IhAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAA9vPxY1mVIdrnM1
FXmLRx3jmFIi+JtXJP+fWyf/oV8o9p5hKJ+SXiQl3YZXAHFMDwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMBAQAsEgsAHQsHESUNCFchDAiVHQoGrj8h
DIScVR2wWSkRuX5BHP+UUCH/mFQi/5lXI/+dWyT/oWIn/6FoJ9SfaiNVjWMiBpVnJAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAmCAUAUzQhADYVDDY5GQ67NRQL5xwI
BKsWCQagQSEOen9DGbA4GAq5WSoS/3tAGP+UVCD/m1sj/55fJP+hYyX/pGkm/6ZvJvOkcymRnXUnG6x4
LgCGcBoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGwgBAFgoEwBIHw86UCQS2U8l
E/9IHxD5KAwGsxcKBZooEgd3NhgIrzMWB7lVKRD/ZTMT/3E8Fv+OUx//mFwh/51kIv+lbSX/qHQn/6p5
J/6qfSnHqIIrRZuBLAOhgCsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABUJREAUSMRGF0u
FsNiMRj/Xi4Y/1krFf9AGwvMFw8jeRwfZ3ciHFHDJRlAlkQfEtRaLA77ZTQS/3hEF/+VXB//m2Mh/59p
Iv+ldCX/rX8o/7GFKv+yiivsr48sf6uRKhOylS8AhXQVAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIdM
IwBsNBhzcjwc/m87HP9rOBr/ZTIX6z4uW58bNNKQGy/GfBokoH0RJLuBFB+glEAhKrBhMhD4ZjYRs4tW
GHeaZh/SoG0h/6NyI/+peyb/sYkp/7eSK/+4lyz8u54uubukMTV3azYBqJQxAAAAAAAAAAAAAAAAAAAA
AAAAAAAAbzYYE35EIMaCRyL/f0Ui/3pCHftVPU2mIEvajRws1mkfIcaKJxpyqDYcJ6ogF1xyEhaOkk4o
G7ZYLQtKYzYTAI1gGSObbB+apXYj9Kp+Jf+thyf/tZUq/7ygL//ApjD/xbA14cO2OGW8tj8HvrU+AJqR
LwCakC8DmY8vCpyUMACESRs4i08g34xPIvKMTyT7h0wk2S9Wt5QeQ9t6JDPp5yQu7/8gJMr+KyBp/0Ei
HtAZE2J1JRtekkYkCDBDIg4AjmgbAIReGAeddSFXqYEk0bCMJv+zlCr/vaQv/8KvMf/HtzX/zcI89sjC
OmbNuDkAl4QvDaKQKxmqmStSrJosBY5SHVKGThW3fkcOoYJHELB6UzanKGvYiCJL46MlR+z/Jj/r6yAy
yfwWHIP/PitD/jIZIIoSGIWIKyY3GiwlMQAAAAAAimghAMKnKQCdeyQhrI0lmraaKvi8pi3/xLYz/8i9
Nf/Ryjz/zsw9wqGSJyaiiyk2pY8oQbKjLleyozAcll0lUo9ZGrKBTwqUgUwHk2xXRJAnc9uEJGDpsiVc
7fclVOCSHkLI7hYnj/89M1b/OR4ZmQwVjIUcHWQSHR1gAAAAAAAAAAAAAAAAAKKMIgCfiSEQtqArx76r
L//HvDX/ysM2/9HPPf/T0kDUpo8mbKeOJy+umS1Vtqc0RrSoNTmmbi85o2wms5BhF5iMYhqLT3CHZiN4
4IIjb+qnJm7x8iVq7+UgVtLtGTeZ7kE0RPAzGRqRDRSRiBoeeSIjJoUAISlrAAAAAACnmCsAn5MpCbGf
LGG7qS7pwrQy/8vEN//NyTf/1NNA/9TTP7WokCpSqZAoNaydK1i3rjo8urI5Sqt2MxW3gTSop3crnJhw
JotoeXFkOIDIapKIc4KTgnG1hnNosnVgWLA/MjiuLhkSsiUUGG8XHYJxKCdlHzYyVgCZjCcA//9tAKqb
KCm3pSyivqww9sGzMf/IwTb/zso4/9HPO//U0kD0u64wjqaMKESqkyM2sKQqV7iyPjq7tjtN97ZOAMaT
RW69izqspXkrkrKCO7qvgESPtoQ1WqNsKYuJUBqgekQTokUhCqEmEAeJLRoKVGtQGqRnURg9cFsZAJyR
Jwqvni1huqcu2sCuMf/DtTP/ysE2/8/LOP/Rzjn/1NJBz8O9PWmfhSZZq5UxT7GfNTS6sj1ZwL1BQMC+
QkjLnFAAyptSGs+dS6XMmkrOzZhM/sWPSLFWMw9FUiYGjEwZBMRCFQTKOxMEzDAOBX49Iw1Ef2Md0495
Jo+xojU8tqQwpbyrMffBsTL/xbk0/8vENv/PzDj/0M4698/PP57S00cgoYovQaONMUGslzZdua4/M765
RlHCwkVOwMFDMKiSaADpsVkA2KldOeKtWtjdqVb/059SsWdEGVVuRA+gaDcL0VYmCNVNIAjXPhgIkUYq
D02TfCfTsJsy/LupNPO/rzT+w7Y1/8m/Nv/NyTj/z8w6/8/OPN/OzkFk0NdZB5yKLR+ijC9jrpw7O6+d
PFC/vUZMvr1FOsXGS2DDwU8SAAAAANyoXADmwm8A3q9gN+KwXMPdqleuglsbUX9TFJ5+ThLWcT8O1l4y
CtdPLQySXEMYTK2bM9K+rTj/xLY4/8i9Of/Nxjr/0Ms7/9DOPfvRzkKxzM1GMQAA/wCfizAtpI4zaKWS
NTStnjpktq9CLMLBS2LExkk1xcdIVr7WNQAAAAAAAAAAALScdQDmsVoA16phFtepWjabcyxQmWwioZdm
INuIWxrfd08X43NTHap6YyNYuak+18q+QP/Oxj7/0Mk9/9HNPv/S0ELn0tBGdszNUA/KyUkAfnc1AZyG
LzColDY3rJ07Y6yeOy+9vkVbv8BINMXITmDEx0whxcdLAAAAAAAAAAAAAAAAAAAAAACQeEgAtYcyAKV+
NF6uhTTXqX8x3Zx0KeOQayjxh2Yn7ZN+LX/DtkHtz8ZC/9TNQ//W0UX91dJJvtHRSj3BwmcCycpXAAAA
AACokzAAqJMwFKuYM1iupDwrwr5HV8C8R0LHyU5Ix8lPTOLQQAC7xlIAAAAAAAAAAAAAAAAAAAAAAAAA
AACrhz8AqIZAFbCNQpqxjD/erIg756B9NemJbSuXwbdEr9bPTP/a1Uz/2ddM7dbUTYPPzVMV1NNQAMPA
YQAAAAAAAAAAAKSSMACRfSICvbdEKL68RGTCv0Y8yMhSQsjHUF/Fx00HxchNAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAH57ZACoiUQAo4hJEayKQ06phz1qnX03QqucPizKwkmq1tJQzNfWT6fS0k5EwcRSA8jK
UQAAAAAAAAAAAAAAAAAAAAAAsbZMAPvQYQC8uEUlwcBILMfJT1LIyVJXx8dSCsfHUQAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC9sHYAutQ8AMK6WgbCwV4Pur9gBd7K
TwA3jrQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwcJQAMHCUhDFyU5Yx8pNNcTHVgTEyVIAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC/v1AAv79RBMDDUAjQwD4ArrxkAAAA
AAAAAAAAAAAAAAAAAAD4Af///AB///wAP//wAA//4AAH/8AAAf+AAAD/gAAAPwABABkAAYAQAAHgAAAB
8AAAAeAAAAHAAIABAACAAAAAwAAAAOAAAgHwAAQB/AAOA/wAPgP+AH8H//H/D////z8=
</value>
</data>
</root>

View File

@@ -16,6 +16,13 @@ namespace RadioDJViewer
public string OutputFolder { get; set; }
public string OutputImageName { get; set; }
public string UrlFormat { get; set; }
public int PollingRateSeconds { get; set; } // Add polling rate property
public string MarqueeScrollSpeed { get; set; } // "Very Slow", etc.
public int MarqueePauseTime { get; set; } // in seconds
public string MarqueeSeparator { get; set; } // e.g. " | "
// Web server settings
public bool WebServerEnabled { get; set; }
public int WebServerPort { get; set; }
}
public static class ProfileStorage

View File

@@ -6,9 +6,9 @@ using System.Runtime.InteropServices;
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("RadioDJViewer")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyDescription("RadioDJ Metadata OBS Extractor")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyCompany("Smartcraft Media Group")]
[assembly: AssemblyProduct("RadioDJViewer")]
[assembly: AssemblyCopyright("Copyright © 2025")]
[assembly: AssemblyTrademark("")]
@@ -29,5 +29,5 @@ using System.Runtime.InteropServices;
// Build Number
// Revision
//
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyVersion("1.9.0")]
[assembly: AssemblyFileVersion("1.9.0")]

View File

@@ -80,6 +80,26 @@ namespace RadioDJViewer.Properties {
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap.
/// </summary>
internal static System.Drawing.Bitmap MdiWeatherNight {
get {
object obj = ResourceManager.GetObject("MdiWeatherNight", resourceCulture);
return ((System.Drawing.Bitmap)(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap.
/// </summary>
internal static System.Drawing.Bitmap MdiWeatherSunny {
get {
object obj = ResourceManager.GetObject("MdiWeatherSunny", resourceCulture);
return ((System.Drawing.Bitmap)(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap.
/// </summary>

View File

@@ -124,6 +124,12 @@
<data name="green" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\green.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="MdiWeatherNight" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\MdiWeatherNight.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="MdiWeatherSunny" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\MdiWeatherSunny.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="red" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\red.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>

View File

@@ -12,6 +12,22 @@
<FileAlignment>512</FileAlignment>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<Deterministic>true</Deterministic>
<ApplicationIcon>icon.ico</ApplicationIcon>
<IsWebBootstrapper>false</IsWebBootstrapper>
<PublishUrl>publish\</PublishUrl>
<Install>true</Install>
<InstallFrom>Disk</InstallFrom>
<UpdateEnabled>false</UpdateEnabled>
<UpdateMode>Foreground</UpdateMode>
<UpdateInterval>7</UpdateInterval>
<UpdateIntervalUnits>Days</UpdateIntervalUnits>
<UpdatePeriodically>false</UpdatePeriodically>
<UpdateRequired>false</UpdateRequired>
<MapFileExtensions>true</MapFileExtensions>
<ApplicationRevision>0</ApplicationRevision>
<ApplicationVersion>1.0.0.%2a</ApplicationVersion>
<UseApplicationTrust>false</UseApplicationTrust>
<BootstrapperEnabled>true</BootstrapperEnabled>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
@@ -32,6 +48,9 @@
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup>
<ApplicationIcon>icon.ico</ApplicationIcon>
</PropertyGroup>
<ItemGroup>
<Reference Include="Newtonsoft.Json, Version=13.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\packages\Newtonsoft.Json.13.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
@@ -49,6 +68,7 @@
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="EmbeddedResourceHelper.cs" />
<Compile Include="ProfileStorage.cs" />
<Compile Include="radiodj-restapi-template.cs">
<SubType>Form</SubType>
@@ -64,6 +84,7 @@
</Compile>
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="WidgetServer.cs" />
<EmbeddedResource Include="radiodj-restapi-template.resx">
<DependentUpon>radiodj-restapi-template.cs</DependentUpon>
</EmbeddedResource>
@@ -103,5 +124,25 @@
<ItemGroup>
<None Include="Resources\fallback.png" />
</ItemGroup>
<ItemGroup>
<Content Include="icon.ico" />
<EmbeddedResource Include="index.html" />
<EmbeddedResource Include="script.js" />
<EmbeddedResource Include="style.css" />
<None Include="Resources\MdiWeatherSunny.png" />
<None Include="Resources\MdiWeatherNight.png" />
</ItemGroup>
<ItemGroup>
<BootstrapperPackage Include=".NETFramework,Version=v4.7.2">
<Visible>False</Visible>
<ProductName>Microsoft .NET Framework 4.7.2 %28x86 and x64%29</ProductName>
<Install>true</Install>
</BootstrapperPackage>
<BootstrapperPackage Include="Microsoft.Net.Framework.3.5.SP1">
<Visible>False</Visible>
<ProductName>.NET Framework 3.5 SP1</ProductName>
<Install>false</Install>
</BootstrapperPackage>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

View File

@@ -0,0 +1,225 @@
using System;
using System.IO;
using System.Net;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace RadioDJViewer
{
/// <summary>
/// Simple embedded-resource-backed HTTP widget server.
/// Serves:
/// - / or /index.html -> embedded index.html with {{ARTIST}} and {{TITLE}} replaced
/// - /style.css -> embedded style.css
/// - /script.js -> embedded script.js
/// - /albumart -> the current album art image bytes (uses provided delegate to locate file)
/// Runs on a background Task and can be stopped via Stop().
/// </summary>
public class WidgetServer
{
// In-memory cache for fast serving
public static string CachedArtist = "";
public static string CachedTitle = "";
public static byte[] CachedImageBytes = null;
private readonly int port;
private readonly Func<string> getArtist;
private readonly Func<string> getTitle;
private readonly Func<string> getAlbumArtPath;
private readonly HttpListener listener;
private CancellationTokenSource cts;
private readonly string htmlTemplate;
private readonly string cssText;
private readonly string jsText;
public WidgetServer(int port, Func<string> getArtist, Func<string> getTitle, Func<string> getAlbumArtPath)
{
this.port = port;
this.getArtist = getArtist ?? (() => string.Empty);
this.getTitle = getTitle ?? (() => string.Empty);
this.getAlbumArtPath = getAlbumArtPath ?? (() => string.Empty);
listener = new HttpListener();
listener.Prefixes.Add($"http://+:{port}/");
// Preload embedded resources
htmlTemplate = EmbeddedResourceHelper.Read("index.html");
cssText = EmbeddedResourceHelper.Read("style.css");
jsText = EmbeddedResourceHelper.Read("script.js");
}
public void Start()
{
if (cts != null) return; // already started
cts = new CancellationTokenSource();
listener.Start();
Task.Run(() => RunAsync(cts.Token));
}
public void Stop()
{
try
{
cts?.Cancel();
listener.Stop();
}
catch { }
finally
{
cts = null;
}
}
private async Task RunAsync(CancellationToken token)
{
while (!token.IsCancellationRequested)
{
HttpListenerContext ctx = null;
try
{
ctx = await listener.GetContextAsync().ConfigureAwait(false);
}
catch (HttpListenerException)
{
// listener closed
break;
}
catch (ObjectDisposedException)
{
break;
}
catch (Exception)
{
continue;
}
_ = Task.Run(() => HandleContext(ctx), token);
}
}
private void HandleContext(HttpListenerContext ctx)
{
try
{
var req = ctx.Request;
var resp = ctx.Response;
string path = req.Url.AbsolutePath.TrimEnd('/');
if (string.IsNullOrEmpty(path) || path == "") path = "/";
if (path == "/" || path.Equals("/index.html", StringComparison.OrdinalIgnoreCase))
{
var html = htmlTemplate ?? "";
// Replace placeholders with in-memory cache values
html = html.Replace("{{ARTIST}}", WebUtility.HtmlEncode(CachedArtist ?? string.Empty))
.Replace("{{TITLE}}", WebUtility.HtmlEncode(CachedTitle ?? string.Empty));
var buf = Encoding.UTF8.GetBytes(html);
resp.ContentType = "text/html; charset=utf-8";
resp.ContentLength64 = buf.Length;
resp.OutputStream.Write(buf, 0, buf.Length);
}
else if (path.Equals("/artist.txt", StringComparison.OrdinalIgnoreCase))
{
var buf = Encoding.UTF8.GetBytes(CachedArtist ?? string.Empty);
resp.ContentType = "text/plain; charset=utf-8";
resp.ContentLength64 = buf.Length;
resp.OutputStream.Write(buf, 0, buf.Length);
}
else if (path.Equals("/title.txt", StringComparison.OrdinalIgnoreCase))
{
var buf = Encoding.UTF8.GetBytes(CachedTitle ?? string.Empty);
resp.ContentType = "text/plain; charset=utf-8";
resp.ContentLength64 = buf.Length;
resp.OutputStream.Write(buf, 0, buf.Length);
}
else if (path.Equals("/style.css", StringComparison.OrdinalIgnoreCase))
{
var buf = Encoding.UTF8.GetBytes(cssText ?? "");
resp.ContentType = "text/css; charset=utf-8";
resp.ContentLength64 = buf.Length;
resp.OutputStream.Write(buf, 0, buf.Length);
}
else if (path.Equals("/script.js", StringComparison.OrdinalIgnoreCase))
{
var buf = Encoding.UTF8.GetBytes(jsText ?? "");
resp.ContentType = "application/javascript; charset=utf-8";
resp.ContentLength64 = buf.Length;
resp.OutputStream.Write(buf, 0, buf.Length);
}
else if (path.Equals("/Album-Art.png", StringComparison.OrdinalIgnoreCase) || path.Equals("/album-art.png", StringComparison.OrdinalIgnoreCase))
{
if (CachedImageBytes != null && CachedImageBytes.Length > 0)
{
try
{
resp.ContentType = "image/png";
resp.ContentLength64 = CachedImageBytes.Length;
resp.OutputStream.Write(CachedImageBytes, 0, CachedImageBytes.Length);
}
catch
{
ServeFallbackImage(resp);
}
}
else
{
ServeFallbackImage(resp);
}
}
else
{
resp.StatusCode = 404;
var msg = Encoding.UTF8.GetBytes("Not found");
resp.OutputStream.Write(msg, 0, msg.Length);
}
resp.OutputStream.Close();
}
catch
{
try { ctx.Response.OutputStream.Close(); } catch { }
}
}
private void ServeFallbackImage(HttpListenerResponse resp)
{
try
{
var img = Properties.Resources.fallback;
if (img != null)
{
using (var ms = new MemoryStream())
{
img.Save(ms, System.Drawing.Imaging.ImageFormat.Png);
var buf = ms.ToArray();
resp.ContentType = "image/png";
resp.ContentLength64 = buf.Length;
resp.OutputStream.Write(buf, 0, buf.Length);
}
}
else
{
resp.StatusCode = 404;
var msg = Encoding.UTF8.GetBytes("No image");
resp.OutputStream.Write(msg, 0, msg.Length);
}
}
catch
{
resp.StatusCode = 500;
}
}
private string GetMimeTypeFromPath(string path)
{
var ext = Path.GetExtension(path).ToLowerInvariant();
switch (ext)
{
case ".png": return "image/png";
case ".jpg":
case ".jpeg": return "image/jpeg";
case ".gif": return "image/gif";
default: return "application/octet-stream";
}
}
}
}

BIN
RadioDJViewer/icon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

36
RadioDJViewer/index.html Normal file
View File

@@ -0,0 +1,36 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Radio DJ Now Playing</title>
<link rel="stylesheet" href="./style.css">
</head>
<body>
<div class="widget-container">
<!-- Blurred background layer -->
<div class="background-blur">
<img class="background-image" src="Album-Art.png" alt="Album backdrop">
</div>
<!-- Dark overlay for contrast -->
<div class="overlay"></div>
<!-- Content layer -->
<div class="widget-content">
<!-- Album art (left) -->
<div class="album-art-box">
<img class="album-art" src="Album-Art.png" alt="Album art">
</div>
<!-- Song info (right) -->
<div class="song-info">
<div class="song-title">Loading...</div>
<div class="song-artist">Loading...</div>
</div>
</div>
</div>
<script src="./script.js"></script>
</body>
</html>

View File

@@ -28,35 +28,51 @@
/// </summary>
private void InitializeComponent()
{
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(Form1));
this.groupBox1 = new System.Windows.Forms.GroupBox();
this.textBox1 = new System.Windows.Forms.TextBox();
this.label1 = new System.Windows.Forms.Label();
this.button1 = new System.Windows.Forms.Button();
this.ok_button_master = new System.Windows.Forms.Button();
this.button2 = new System.Windows.Forms.Button();
this.groupBox2 = new System.Windows.Forms.GroupBox();
this.button5 = new System.Windows.Forms.Button();
this.textBox6 = new System.Windows.Forms.TextBox();
this.label11 = new System.Windows.Forms.Label();
this.comboBox1 = new System.Windows.Forms.ComboBox();
this.label2 = new System.Windows.Forms.Label();
this.label3 = new System.Windows.Forms.Label();
this.textBox2 = new System.Windows.Forms.TextBox();
this.label4 = new System.Windows.Forms.Label();
this.textBox3 = new System.Windows.Forms.TextBox();
this.label5 = new System.Windows.Forms.Label();
this.textBox4 = new System.Windows.Forms.TextBox();
this.button3 = new System.Windows.Forms.Button();
this.button4 = new System.Windows.Forms.Button();
this.groupBox3 = new System.Windows.Forms.GroupBox();
this.buttonSelectOutputFolder = new System.Windows.Forms.Button();
this.labelOutputFolderPath = new System.Windows.Forms.Label();
this.textBox5 = new System.Windows.Forms.TextBox();
this.label10 = new System.Windows.Forms.Label();
this.button4 = new System.Windows.Forms.Button();
this.button3 = new System.Windows.Forms.Button();
this.textBox4 = new System.Windows.Forms.TextBox();
this.label5 = new System.Windows.Forms.Label();
this.textBox3 = new System.Windows.Forms.TextBox();
this.label4 = new System.Windows.Forms.Label();
this.textBox2 = new System.Windows.Forms.TextBox();
this.label3 = new System.Windows.Forms.Label();
this.label2 = new System.Windows.Forms.Label();
this.comboBox1 = new System.Windows.Forms.ComboBox();
this.label11 = new System.Windows.Forms.Label();
this.textBox6 = new System.Windows.Forms.TextBox();
this.button6 = new System.Windows.Forms.Button();
this.label6 = new System.Windows.Forms.Label();
this.label7 = new System.Windows.Forms.Label();
this.textBox7 = new System.Windows.Forms.TextBox();
this.groupBox2 = new System.Windows.Forms.GroupBox();
this.label13 = new System.Windows.Forms.Label();
this.comboBoxScrollSpeed = new System.Windows.Forms.ComboBox();
this.textBoxPauseTime = new System.Windows.Forms.TextBox();
this.separatingcharacterformatbox = new System.Windows.Forms.TextBox();
this.textBox9 = new System.Windows.Forms.TextBox();
this.label9 = new System.Windows.Forms.Label();
this.label8 = new System.Windows.Forms.Label();
this.label12 = new System.Windows.Forms.Label();
this.web_server_label = new System.Windows.Forms.Label();
this.web_server_checkbox = new System.Windows.Forms.CheckBox();
this.web_server_settings_groupbox = new System.Windows.Forms.GroupBox();
this.web_server_port_label = new System.Windows.Forms.Label();
this.web_server_port_textbox = new System.Windows.Forms.TextBox();
this.groupBox1.SuspendLayout();
this.groupBox2.SuspendLayout();
this.groupBox3.SuspendLayout();
this.groupBox2.SuspendLayout();
this.web_server_settings_groupbox.SuspendLayout();
this.SuspendLayout();
//
// groupBox1
@@ -86,73 +102,106 @@
this.label1.TabIndex = 0;
this.label1.Text = "URL Format:";
//
// button1
// ok_button_master
//
this.button1.Location = new System.Drawing.Point(12, 569);
this.button1.Name = "button1";
this.button1.Size = new System.Drawing.Size(157, 23);
this.button1.TabIndex = 1;
this.button1.Text = "Save";
this.button1.UseVisualStyleBackColor = true;
this.ok_button_master.Location = new System.Drawing.Point(65, 648);
this.ok_button_master.Name = "ok_button_master";
this.ok_button_master.Size = new System.Drawing.Size(157, 23);
this.ok_button_master.TabIndex = 1;
this.ok_button_master.Text = "OK";
this.ok_button_master.UseVisualStyleBackColor = true;
//
// button2
//
this.button2.Location = new System.Drawing.Point(259, 569);
this.button2.Location = new System.Drawing.Point(305, 648);
this.button2.Name = "button2";
this.button2.Size = new System.Drawing.Size(157, 23);
this.button2.TabIndex = 2;
this.button2.Text = "Cancel";
this.button2.UseVisualStyleBackColor = true;
//
// groupBox2
// comboBox1
//
this.groupBox2.Controls.Add(this.label6);
this.groupBox2.Controls.Add(this.button6);
this.groupBox2.Controls.Add(this.button5);
this.groupBox2.Controls.Add(this.textBox6);
this.groupBox2.Controls.Add(this.label11);
this.groupBox2.Controls.Add(this.groupBox3);
this.groupBox2.Controls.Add(this.button4);
this.groupBox2.Controls.Add(this.button3);
this.groupBox2.Controls.Add(this.textBox4);
this.groupBox2.Controls.Add(this.label5);
this.groupBox2.Controls.Add(this.textBox3);
this.groupBox2.Controls.Add(this.label4);
this.groupBox2.Controls.Add(this.textBox2);
this.groupBox2.Controls.Add(this.label3);
this.groupBox2.Controls.Add(this.label2);
this.groupBox2.Controls.Add(this.comboBox1);
this.groupBox2.Location = new System.Drawing.Point(12, 100);
this.groupBox2.Name = "groupBox2";
this.groupBox2.Size = new System.Drawing.Size(519, 470);
this.groupBox2.TabIndex = 3;
this.groupBox2.TabStop = false;
this.groupBox2.Text = "Profiles";
this.comboBox1.FormattingEnabled = true;
this.comboBox1.Location = new System.Drawing.Point(120, 27);
this.comboBox1.Name = "comboBox1";
this.comboBox1.Size = new System.Drawing.Size(290, 21);
this.comboBox1.TabIndex = 0;
//
// button5
// label2
//
this.button5.Location = new System.Drawing.Point(322, 419);
this.button5.Name = "button5";
this.button5.Size = new System.Drawing.Size(120, 30);
this.button5.TabIndex = 16;
this.button5.Text = "Load Profile";
this.button5.UseVisualStyleBackColor = true;
this.label2.AutoSize = true;
this.label2.Location = new System.Drawing.Point(20, 30);
this.label2.Name = "label2";
this.label2.Size = new System.Drawing.Size(41, 13);
this.label2.TabIndex = 1;
this.label2.Text = "Profiles";
//
// textBox6
// label3
//
this.textBox6.Location = new System.Drawing.Point(120, 62);
this.textBox6.Name = "textBox6";
this.textBox6.Size = new System.Drawing.Size(290, 20);
this.textBox6.TabIndex = 15;
this.label3.AutoSize = true;
this.label3.Location = new System.Drawing.Point(20, 100);
this.label3.Name = "label3";
this.label3.Size = new System.Drawing.Size(57, 13);
this.label3.TabIndex = 2;
this.label3.Text = "IP address";
//
// label11
// textBox2
//
this.label11.AutoSize = true;
this.label11.Location = new System.Drawing.Point(20, 65);
this.label11.Name = "label11";
this.label11.Size = new System.Drawing.Size(67, 13);
this.label11.TabIndex = 14;
this.label11.Text = "Profile Name";
this.textBox2.Location = new System.Drawing.Point(120, 97);
this.textBox2.Name = "textBox2";
this.textBox2.Size = new System.Drawing.Size(290, 20);
this.textBox2.TabIndex = 3;
//
// label4
//
this.label4.AutoSize = true;
this.label4.Location = new System.Drawing.Point(20, 135);
this.label4.Name = "label4";
this.label4.Size = new System.Drawing.Size(26, 13);
this.label4.TabIndex = 4;
this.label4.Text = "Port";
//
// textBox3
//
this.textBox3.Location = new System.Drawing.Point(120, 132);
this.textBox3.Name = "textBox3";
this.textBox3.Size = new System.Drawing.Size(290, 20);
this.textBox3.TabIndex = 5;
//
// label5
//
this.label5.AutoSize = true;
this.label5.Location = new System.Drawing.Point(20, 170);
this.label5.Name = "label5";
this.label5.Size = new System.Drawing.Size(53, 13);
this.label5.TabIndex = 6;
this.label5.Text = "Password";
//
// textBox4
//
this.textBox4.Location = new System.Drawing.Point(120, 167);
this.textBox4.Name = "textBox4";
this.textBox4.Size = new System.Drawing.Size(290, 20);
this.textBox4.TabIndex = 7;
//
// button3
//
this.button3.Location = new System.Drawing.Point(71, 491);
this.button3.Name = "button3";
this.button3.Size = new System.Drawing.Size(120, 30);
this.button3.TabIndex = 8;
this.button3.Text = "Save Profile";
this.button3.UseVisualStyleBackColor = true;
//
// button4
//
this.button4.Location = new System.Drawing.Point(316, 491);
this.button4.Name = "button4";
this.button4.Size = new System.Drawing.Size(120, 30);
this.button4.TabIndex = 9;
this.button4.Text = "Remove Profile";
this.button4.UseVisualStyleBackColor = true;
//
// groupBox3
//
@@ -160,9 +209,9 @@
this.groupBox3.Controls.Add(this.labelOutputFolderPath);
this.groupBox3.Controls.Add(this.textBox5);
this.groupBox3.Controls.Add(this.label10);
this.groupBox3.Location = new System.Drawing.Point(20, 258);
this.groupBox3.Location = new System.Drawing.Point(31, 370);
this.groupBox3.Name = "groupBox3";
this.groupBox3.Size = new System.Drawing.Size(482, 135);
this.groupBox3.Size = new System.Drawing.Size(482, 115);
this.groupBox3.TabIndex = 12;
this.groupBox3.TabStop = false;
this.groupBox3.Text = "Output Setting";
@@ -179,7 +228,7 @@
// labelOutputFolderPath
//
this.labelOutputFolderPath.AutoSize = true;
this.labelOutputFolderPath.Location = new System.Drawing.Point(187, 46);
this.labelOutputFolderPath.Location = new System.Drawing.Point(166, 41);
this.labelOutputFolderPath.Name = "labelOutputFolderPath";
this.labelOutputFolderPath.Size = new System.Drawing.Size(93, 13);
this.labelOutputFolderPath.TabIndex = 16;
@@ -187,106 +236,39 @@
//
// textBox5
//
this.textBox5.Location = new System.Drawing.Point(72, 87);
this.textBox5.Location = new System.Drawing.Point(149, 80);
this.textBox5.Name = "textBox5";
this.textBox5.Size = new System.Drawing.Size(350, 20);
this.textBox5.Size = new System.Drawing.Size(247, 20);
this.textBox5.TabIndex = 16;
//
// label10
//
this.label10.AutoSize = true;
this.label10.Location = new System.Drawing.Point(157, 71);
this.label10.Location = new System.Drawing.Point(17, 83);
this.label10.Name = "label10";
this.label10.Size = new System.Drawing.Size(181, 13);
this.label10.Size = new System.Drawing.Size(126, 13);
this.label10.TabIndex = 15;
this.label10.Text = "Output Image save name and loction";
this.label10.Text = "Output Image save name";
//
// button4
// label11
//
this.button4.Location = new System.Drawing.Point(180, 419);
this.button4.Name = "button4";
this.button4.Size = new System.Drawing.Size(120, 30);
this.button4.TabIndex = 9;
this.button4.Text = "Remove Profile";
this.button4.UseVisualStyleBackColor = true;
this.label11.AutoSize = true;
this.label11.Location = new System.Drawing.Point(20, 65);
this.label11.Name = "label11";
this.label11.Size = new System.Drawing.Size(67, 13);
this.label11.TabIndex = 14;
this.label11.Text = "Profile Name";
//
// button3
// textBox6
//
this.button3.Location = new System.Drawing.Point(40, 419);
this.button3.Name = "button3";
this.button3.Size = new System.Drawing.Size(120, 30);
this.button3.TabIndex = 8;
this.button3.Text = "Save Profile";
this.button3.UseVisualStyleBackColor = true;
//
// textBox4
//
this.textBox4.Location = new System.Drawing.Point(120, 167);
this.textBox4.Name = "textBox4";
this.textBox4.Size = new System.Drawing.Size(290, 20);
this.textBox4.TabIndex = 7;
//
// label5
//
this.label5.AutoSize = true;
this.label5.Location = new System.Drawing.Point(20, 170);
this.label5.Name = "label5";
this.label5.Size = new System.Drawing.Size(53, 13);
this.label5.TabIndex = 6;
this.label5.Text = "Password";
//
// textBox3
//
this.textBox3.Location = new System.Drawing.Point(120, 132);
this.textBox3.Name = "textBox3";
this.textBox3.Size = new System.Drawing.Size(290, 20);
this.textBox3.TabIndex = 5;
//
// label4
//
this.label4.AutoSize = true;
this.label4.Location = new System.Drawing.Point(20, 135);
this.label4.Name = "label4";
this.label4.Size = new System.Drawing.Size(26, 13);
this.label4.TabIndex = 4;
this.label4.Text = "Port";
//
// textBox2
//
this.textBox2.Location = new System.Drawing.Point(120, 97);
this.textBox2.Name = "textBox2";
this.textBox2.Size = new System.Drawing.Size(290, 20);
this.textBox2.TabIndex = 3;
//
// label3
//
this.label3.AutoSize = true;
this.label3.Location = new System.Drawing.Point(20, 100);
this.label3.Name = "label3";
this.label3.Size = new System.Drawing.Size(57, 13);
this.label3.TabIndex = 2;
this.label3.Text = "IP address";
//
// label2
//
this.label2.AutoSize = true;
this.label2.Location = new System.Drawing.Point(20, 30);
this.label2.Name = "label2";
this.label2.Size = new System.Drawing.Size(41, 13);
this.label2.TabIndex = 1;
this.label2.Text = "Profiles";
//
// comboBox1
//
this.comboBox1.FormattingEnabled = true;
this.comboBox1.Location = new System.Drawing.Point(120, 27);
this.comboBox1.Name = "comboBox1";
this.comboBox1.Size = new System.Drawing.Size(290, 21);
this.comboBox1.TabIndex = 0;
this.textBox6.Location = new System.Drawing.Point(120, 62);
this.textBox6.Name = "textBox6";
this.textBox6.Size = new System.Drawing.Size(290, 20);
this.textBox6.TabIndex = 15;
//
// button6
//
this.button6.Location = new System.Drawing.Point(23, 202);
this.button6.Location = new System.Drawing.Point(23, 269);
this.button6.Name = "button6";
this.button6.Size = new System.Drawing.Size(140, 23);
this.button6.TabIndex = 18;
@@ -296,29 +278,203 @@
// label6
//
this.label6.AutoSize = true;
this.label6.Location = new System.Drawing.Point(177, 207);
this.label6.Location = new System.Drawing.Point(169, 274);
this.label6.Name = "label6";
this.label6.Size = new System.Drawing.Size(93, 13);
this.label6.TabIndex = 19;
this.label6.Text = "No folder selected";
//
// label7
//
this.label7.AutoSize = true;
this.label7.Location = new System.Drawing.Point(20, 211);
this.label7.Name = "label7";
this.label7.Size = new System.Drawing.Size(124, 13);
this.label7.TabIndex = 20;
this.label7.Text = "Polling Rate (in seconds)";
//
// textBox7
//
this.textBox7.Location = new System.Drawing.Point(150, 208);
this.textBox7.Name = "textBox7";
this.textBox7.Size = new System.Drawing.Size(63, 20);
this.textBox7.TabIndex = 21;
//
// groupBox2
//
this.groupBox2.Controls.Add(this.web_server_settings_groupbox);
this.groupBox2.Controls.Add(this.label13);
this.groupBox2.Controls.Add(this.comboBoxScrollSpeed);
this.groupBox2.Controls.Add(this.textBoxPauseTime);
this.groupBox2.Controls.Add(this.separatingcharacterformatbox);
this.groupBox2.Controls.Add(this.textBox6);
this.groupBox2.Controls.Add(this.label11);
this.groupBox2.Controls.Add(this.textBox9);
this.groupBox2.Controls.Add(this.label9);
this.groupBox2.Controls.Add(this.label8);
this.groupBox2.Controls.Add(this.textBox7);
this.groupBox2.Controls.Add(this.label7);
this.groupBox2.Controls.Add(this.label6);
this.groupBox2.Controls.Add(this.button6);
this.groupBox2.Controls.Add(this.groupBox3);
this.groupBox2.Controls.Add(this.button4);
this.groupBox2.Controls.Add(this.button3);
this.groupBox2.Controls.Add(this.textBox4);
this.groupBox2.Controls.Add(this.label5);
this.groupBox2.Controls.Add(this.textBox3);
this.groupBox2.Controls.Add(this.label4);
this.groupBox2.Controls.Add(this.textBox2);
this.groupBox2.Controls.Add(this.label3);
this.groupBox2.Controls.Add(this.label2);
this.groupBox2.Controls.Add(this.comboBox1);
this.groupBox2.Location = new System.Drawing.Point(12, 100);
this.groupBox2.Name = "groupBox2";
this.groupBox2.Size = new System.Drawing.Size(519, 527);
this.groupBox2.TabIndex = 3;
this.groupBox2.TabStop = false;
this.groupBox2.Text = "Profiles";
//
// label13
//
this.label13.AutoSize = true;
this.label13.Location = new System.Drawing.Point(20, 240);
this.label13.Name = "label13";
this.label13.Size = new System.Drawing.Size(142, 13);
this.label13.TabIndex = 32;
this.label13.Text = "Separating Character Format";
//
// comboBoxScrollSpeed
//
this.comboBoxScrollSpeed.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
this.comboBoxScrollSpeed.FormattingEnabled = true;
this.comboBoxScrollSpeed.Items.AddRange(new object[] {
"Very Slow",
"Slow",
"Medium",
"Fast",
"Very Fast"});
this.comboBoxScrollSpeed.Location = new System.Drawing.Point(382, 207);
this.comboBoxScrollSpeed.Name = "comboBoxScrollSpeed";
this.comboBoxScrollSpeed.Size = new System.Drawing.Size(97, 21);
this.comboBoxScrollSpeed.TabIndex = 29;
//
// textBoxPauseTime
//
this.textBoxPauseTime.Location = new System.Drawing.Point(382, 237);
this.textBoxPauseTime.Name = "textBoxPauseTime";
this.textBoxPauseTime.Size = new System.Drawing.Size(97, 20);
this.textBoxPauseTime.TabIndex = 30;
//
// separatingcharacterformatbox
//
this.separatingcharacterformatbox.Location = new System.Drawing.Point(164, 237);
this.separatingcharacterformatbox.Name = "separatingcharacterformatbox";
this.separatingcharacterformatbox.Size = new System.Drawing.Size(49, 20);
this.separatingcharacterformatbox.TabIndex = 31;
//
// textBox9
//
this.textBox9.Location = new System.Drawing.Point(382, 237);
this.textBox9.Name = "textBox9";
this.textBox9.Size = new System.Drawing.Size(97, 20);
this.textBox9.TabIndex = 25;
//
// label9
//
this.label9.AutoSize = true;
this.label9.Location = new System.Drawing.Point(239, 240);
this.label9.Name = "label9";
this.label9.Size = new System.Drawing.Size(119, 13);
this.label9.TabIndex = 24;
this.label9.Text = "Pause time (in seconds)";
//
// label8
//
this.label8.AutoSize = true;
this.label8.Location = new System.Drawing.Point(239, 211);
this.label8.Name = "label8";
this.label8.Size = new System.Drawing.Size(127, 13);
this.label8.TabIndex = 22;
this.label8.Text = "Scroll Speed (in Main UI)";
this.label8.Click += new System.EventHandler(this.label8_Click);
//
// label12
//
this.label12.AutoSize = true;
this.label12.Location = new System.Drawing.Point(20, 240);
this.label12.Name = "label12";
this.label12.Size = new System.Drawing.Size(138, 13);
this.label12.TabIndex = 27;
this.label12.Text = "Separating character format";
//
// web_server_label
//
this.web_server_label.AutoSize = true;
this.web_server_label.Location = new System.Drawing.Point(44, 24);
this.web_server_label.Name = "web_server_label";
this.web_server_label.Size = new System.Drawing.Size(67, 13);
this.web_server_label.TabIndex = 33;
this.web_server_label.Text = "Web Server:";
//
// web_server_checkbox
//
this.web_server_checkbox.AutoSize = true;
this.web_server_checkbox.Location = new System.Drawing.Point(111, 23);
this.web_server_checkbox.Name = "web_server_checkbox";
this.web_server_checkbox.Size = new System.Drawing.Size(65, 17);
this.web_server_checkbox.TabIndex = 34;
this.web_server_checkbox.Text = "Enabled";
this.web_server_checkbox.UseVisualStyleBackColor = true;
//
// web_server_settings_groupbox
//
this.web_server_settings_groupbox.Controls.Add(this.web_server_port_textbox);
this.web_server_settings_groupbox.Controls.Add(this.web_server_port_label);
this.web_server_settings_groupbox.Controls.Add(this.web_server_label);
this.web_server_settings_groupbox.Controls.Add(this.web_server_checkbox);
this.web_server_settings_groupbox.Location = new System.Drawing.Point(31, 298);
this.web_server_settings_groupbox.Name = "web_server_settings_groupbox";
this.web_server_settings_groupbox.Size = new System.Drawing.Size(396, 58);
this.web_server_settings_groupbox.TabIndex = 35;
this.web_server_settings_groupbox.TabStop = false;
this.web_server_settings_groupbox.Text = "Web Server Settings";
//
// web_server_port_label
//
this.web_server_port_label.AutoSize = true;
this.web_server_port_label.Location = new System.Drawing.Point(211, 23);
this.web_server_port_label.Name = "web_server_port_label";
this.web_server_port_label.Size = new System.Drawing.Size(29, 13);
this.web_server_port_label.TabIndex = 35;
this.web_server_port_label.Text = "Port:";
//
// web_server_port_textbox
//
this.web_server_port_textbox.Location = new System.Drawing.Point(246, 19);
this.web_server_port_textbox.Name = "web_server_port_textbox";
this.web_server_port_textbox.Size = new System.Drawing.Size(77, 20);
this.web_server_port_textbox.TabIndex = 36;
//
// Form1
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(543, 604);
this.ClientSize = new System.Drawing.Size(572, 695);
this.Controls.Add(this.groupBox2);
this.Controls.Add(this.button2);
this.Controls.Add(this.button1);
this.Controls.Add(this.ok_button_master);
this.Controls.Add(this.groupBox1);
this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
this.Name = "Form1";
this.Text = "Settings";
this.groupBox1.ResumeLayout(false);
this.groupBox1.PerformLayout();
this.groupBox2.ResumeLayout(false);
this.groupBox2.PerformLayout();
this.groupBox3.ResumeLayout(false);
this.groupBox3.PerformLayout();
this.groupBox2.ResumeLayout(false);
this.groupBox2.PerformLayout();
this.web_server_settings_groupbox.ResumeLayout(false);
this.web_server_settings_groupbox.PerformLayout();
this.ResumeLayout(false);
}
@@ -328,28 +484,44 @@
private System.Windows.Forms.GroupBox groupBox1;
private System.Windows.Forms.TextBox textBox1;
private System.Windows.Forms.Label label1;
private System.Windows.Forms.Button button1;
private System.Windows.Forms.Button ok_button_master;
private System.Windows.Forms.Button button2;
private System.Windows.Forms.GroupBox groupBox2;
private System.Windows.Forms.Label label3;
private System.Windows.Forms.Label label2;
private System.Windows.Forms.ComboBox comboBox1;
private System.Windows.Forms.Button button4;
private System.Windows.Forms.Button button3;
private System.Windows.Forms.TextBox textBox4;
private System.Windows.Forms.Label label5;
private System.Windows.Forms.TextBox textBox3;
private System.Windows.Forms.Label label4;
private System.Windows.Forms.Label label2;
private System.Windows.Forms.Label label3;
private System.Windows.Forms.TextBox textBox2;
private System.Windows.Forms.Label label4;
private System.Windows.Forms.TextBox textBox3;
private System.Windows.Forms.Label label5;
private System.Windows.Forms.TextBox textBox4;
private System.Windows.Forms.Button button3;
private System.Windows.Forms.Button button4;
private System.Windows.Forms.GroupBox groupBox3;
private System.Windows.Forms.Button buttonSelectOutputFolder;
private System.Windows.Forms.Label labelOutputFolderPath;
private System.Windows.Forms.TextBox textBox5;
private System.Windows.Forms.Label label10;
private System.Windows.Forms.Button button5;
private System.Windows.Forms.TextBox textBox6;
private System.Windows.Forms.Label label11;
private System.Windows.Forms.Label label6;
private System.Windows.Forms.TextBox textBox6;
private System.Windows.Forms.Button button6;
private System.Windows.Forms.Label label6;
private System.Windows.Forms.Label label7;
private System.Windows.Forms.TextBox textBox7;
private System.Windows.Forms.GroupBox groupBox2;
private System.Windows.Forms.Label label8;
private System.Windows.Forms.TextBox textBox9;
private System.Windows.Forms.Label label9;
private System.Windows.Forms.ComboBox comboBox2;
private System.Windows.Forms.TextBox textBox8;
private System.Windows.Forms.Label label12;
private System.Windows.Forms.ComboBox comboBoxScrollSpeed;
private System.Windows.Forms.TextBox textBoxPauseTime;
private System.Windows.Forms.TextBox separatingcharacterformatbox;
private System.Windows.Forms.Label label13;
private System.Windows.Forms.GroupBox web_server_settings_groupbox;
private System.Windows.Forms.Label web_server_label;
private System.Windows.Forms.CheckBox web_server_checkbox;
private System.Windows.Forms.TextBox web_server_port_textbox;
private System.Windows.Forms.Label web_server_port_label;
}
}

View File

@@ -19,12 +19,21 @@ namespace RadioDJViewer
public Form1()
{
InitializeComponent();
textBox4.UseSystemPasswordChar = true; // Obscure password field
comboBoxScrollSpeed.Items.Clear();
comboBoxScrollSpeed.Items.AddRange(new object[] {
"Very Slow",
"Slow",
"Medium",
"Fast",
"Very Fast"
});
comboBoxScrollSpeed.SelectedItem = "Medium";
this.buttonSelectOutputFolder.Click += ButtonSelectOutputFolder_Click;
this.button6.Click += ButtonSelectMainImagesFolder_Click;
this.button3.Click += button3_Click;
this.button1.Click += button1_Click;
this.ok_button_master.Click += button1_Click;
this.button4.Click += button4_Click;
this.button5.Click += button5_Click;
this.button2.Click += button2_Click;
// Populate profile list on open
LoadProfileList();
@@ -64,6 +73,18 @@ namespace RadioDJViewer
label6.Text = mainImagesFolderPath;
textBox5.Text = profile.OutputImageName;
textBox1.Text = profile.UrlFormat;
// Default polling rate to 3 if missing or zero
textBox7.Text = (profile.PollingRateSeconds > 0 ? profile.PollingRateSeconds : 3).ToString();
comboBoxScrollSpeed.SelectedItem = profile.MarqueeScrollSpeed ?? "Medium";
textBoxPauseTime.Text = profile.MarqueePauseTime > 0 ? profile.MarqueePauseTime.ToString() : "6";
separatingcharacterformatbox.Text = profile.MarqueeSeparator ?? " | ";
// Web server settings
try
{
web_server_checkbox.Checked = profile.WebServerEnabled;
web_server_port_textbox.Text = (profile.WebServerPort > 0 ? profile.WebServerPort : 8080).ToString();
}
catch { }
}
}
}
@@ -100,6 +121,8 @@ namespace RadioDJViewer
MessageBox.Show("Profile name is required.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Warning);
return;
}
int pollingRate = 3;
int.TryParse(textBox7.Text, out pollingRate);
var profile = new Profile
{
Name = textBox6.Text,
@@ -109,7 +132,14 @@ namespace RadioDJViewer
OutputFolder = outputFolderPath,
MainImagesFolder = mainImagesFolderPath,
OutputImageName = textBox5.Text,
UrlFormat = textBox1.Text
UrlFormat = textBox1.Text,
PollingRateSeconds = pollingRate,
MarqueeScrollSpeed = comboBoxScrollSpeed.SelectedItem?.ToString() ?? "Medium",
MarqueePauseTime = int.TryParse(textBoxPauseTime.Text, out int pt) ? pt : 6,
MarqueeSeparator = separatingcharacterformatbox.Text,
// Web server settings
WebServerEnabled = web_server_checkbox.Checked,
WebServerPort = int.TryParse(web_server_port_textbox.Text, out int wp) ? wp : 8080
};
ProfileStorage.SaveProfile(profile);
// Refresh profile list
@@ -131,33 +161,6 @@ namespace RadioDJViewer
}
}
private void button5_Click(object sender, EventArgs e)
{
// Load Profile (Load Profile button)
if (comboBox1.SelectedItem != null)
{
var profile = ProfileStorage.LoadProfile(comboBox1.SelectedItem.ToString());
if (profile != null)
{
textBox6.Text = profile.Name;
textBox2.Text = profile.IP;
textBox3.Text = profile.Port;
textBox4.Text = profile.Password;
outputFolderPath = profile.OutputFolder;
mainImagesFolderPath = profile.MainImagesFolder;
labelOutputFolderPath.Text = outputFolderPath;
label6.Text = mainImagesFolderPath;
textBox5.Text = profile.OutputImageName;
textBox1.Text = profile.UrlFormat;
Main mainForm = Application.OpenForms["Main"] as Main;
if (mainForm != null)
{
mainForm.LoadProfile(profile);
}
}
}
}
private void button1_Click(object sender, EventArgs e)
{
// Save (bottom Save button) - save all fields to the currently selected or new profile
@@ -171,5 +174,32 @@ namespace RadioDJViewer
// Cancel (bottom Cancel button) - close the settings window without saving
this.Close();
}
private void label8_Click(object sender, EventArgs e)
{
}
public void ApplyTheme(bool darkMode)
{
Color backColor = darkMode ? Color.FromArgb(32, 32, 32) : SystemColors.Control;
Color foreColor = darkMode ? Color.White : SystemColors.ControlText;
this.BackColor = backColor;
this.ForeColor = foreColor;
foreach (Control c in this.Controls)
{
ApplyThemeRecursive(c, backColor, foreColor);
}
}
private void ApplyThemeRecursive(Control control, Color backColor, Color foreColor)
{
control.BackColor = backColor;
control.ForeColor = foreColor;
foreach (Control child in control.Controls)
{
ApplyThemeRecursive(child, backColor, foreColor);
}
}
}
}

View File

@@ -117,4 +117,63 @@
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<assembly alias="System.Drawing" name="System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
<data name="$this.Icon" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
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
</value>
</data>
</root>

128
RadioDJViewer/script.js Normal file
View File

@@ -0,0 +1,128 @@
///////////////
// PARAMETERS //
///////////////
const queryString = window.location.search;
const urlParams = new URLSearchParams(queryString);
const refreshRate = parseInt(urlParams.get("refreshrate")) || 6; // Default 6 seconds
// Image filename - update this if the backend changes the image path
const IMAGE_FILENAME = "Album-Art.png";
//////////////////
// GLOBAL STATE //
//////////////////
let currentArtist = "";
let currentTitle = "";
let refreshInterval = null;
//////////////////
// REFRESH LOGIC //
//////////////////
/**
* Fetches the artist and title from text files
*/
async function fetchSongData() {
try {
const artistResponse = await fetch("artist.txt");
const titleResponse = await fetch("title.txt");
if (!artistResponse.ok || !titleResponse.ok) {
console.error("Failed to fetch song data");
return null;
}
const artist = (await artistResponse.text()).trim();
const title = (await titleResponse.text()).trim();
return { artist, title };
} catch (error) {
console.error("Error fetching song data:", error);
return null;
}
}
/**
* Updates the DOM elements if the song data has changed
*/
function updateSongDisplay(artist, title) {
const titleElement = document.querySelector(".song-title");
const artistElement = document.querySelector(".song-artist");
let trackChanged = false;
// Only update if data has actually changed
if (title !== currentTitle) {
currentTitle = title;
titleElement.classList.add("updating");
titleElement.textContent = title || "Unknown Title";
trackChanged = true;
setTimeout(() => {
titleElement.classList.remove("updating");
}, 400);
}
if (artist !== currentArtist) {
currentArtist = artist;
artistElement.classList.add("updating");
artistElement.textContent = artist || "Unknown Artist";
trackChanged = true;
setTimeout(() => {
artistElement.classList.remove("updating");
}, 400);
}
// Return whether track changed (to trigger image update)
return trackChanged;
}
/**
* Updates both the album art and background image with cache-busting timestamp
* Only called when track actually changes
*/
function updateAlbumArt() {
const albumArt = document.querySelector(".album-art");
const backgroundImage = document.querySelector(".background-image");
const timestamp = new Date().getTime();
const imageUrl = `${IMAGE_FILENAME}?v=${timestamp}`;
albumArt.src = imageUrl;
backgroundImage.src = imageUrl;
}
/**
* Main refresh function - fetches data and updates if changed
*/
async function refresh() {
const data = await fetchSongData();
if (data) {
const trackChanged = updateSongDisplay(data.artist, data.title);
// Only update images when track actually changes
if (trackChanged) {
updateAlbumArt();
}
}
}
///////////////
// INITIALIZE //
///////////////
function init() {
// Load initial data immediately
refresh();
// Set up refresh interval based on URL parameter or default
refreshInterval = setInterval(refresh, refreshRate * 1000);
console.log(`Radio DJ widget initialized - refreshing every ${refreshRate}s`);
}
// Start when DOM is ready
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", init);
} else {
init();
}

143
RadioDJViewer/style.css Normal file
View File

@@ -0,0 +1,143 @@
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
background: #000;
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", sans-serif;
}
/* Main widget container */
.widget-container {
position: relative;
display: flex;
width: 100%;
max-width: 650px;
aspect-ratio: auto;
height: 200px;
border-radius: 20px;
overflow: hidden;
box-shadow: 0 12px 40px rgba(0, 0, 0, 0.95);
margin: 20px;
}
/* Blurred background layer (z-index: 0) */
.background-blur {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 0;
overflow: hidden;
}
.background-image {
width: 100%;
height: 100%;
object-fit: cover;
filter: blur(40px) brightness(0.6);
}
/* Dark overlay (z-index: 1) */
.overlay {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
z-index: 1;
pointer-events: none;
}
/* Content layer (z-index: 2) */
.widget-content {
position: relative;
display: flex;
gap: 24px;
padding: 20px;
z-index: 2;
width: 100%;
height: 100%;
align-items: center;
}
/* Album art box (left) */
.album-art-box {
flex-shrink: 0;
width: 140px;
height: 140px;
border-radius: 10px;
overflow: hidden;
background: rgba(60, 60, 60, 0.5);
display: flex;
justify-content: center;
align-items: center;
box-shadow: 0 6px 16px rgba(0, 0, 0, 0.6);
}
.album-art {
width: 100%;
height: 100%;
object-fit: cover;
}
/* Song info box (right) */
.song-info {
flex: 1;
display: flex;
flex-direction: column;
justify-content: center;
gap: 4px;
min-width: 0;
padding: 0 12px;
}
/* Song title */
.song-title {
font-size: 30px;
font-weight: 700;
color: #ffffff;
text-shadow: 2px 2px 5px rgba(0, 0, 0, 0.9);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
letter-spacing: -0.5px;
line-height: 1.1;
}
/* Song artist */
.song-artist {
font-size: 22px;
font-weight: 500;
color: #ffffff;
text-shadow: 2px 2px 5px rgba(0, 0, 0, 0.9);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
letter-spacing: -0.3px;
line-height: 1.1;
opacity: 1;
}
/* Smooth fade animation for updates */
@keyframes fadeIn {
from {
opacity: 0.5;
}
to {
opacity: 1;
}
}
.song-title.updating,
.song-artist.updating {
animation: fadeIn 0.4s ease-in-out;
}

BIN
icon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB