commit ab3d1b347241407f6a79ea17ccd8c86caaae6700 Author: ianfijolek Date: Tue Feb 1 14:59:57 2011 -0500 Initial git commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..baf3686 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.hg/ +.hgignore + diff --git a/SubsonicMono/SubsonicAPI/AssemblyInfo.cs b/SubsonicMono/SubsonicAPI/AssemblyInfo.cs new file mode 100644 index 0000000..7f3d04a --- /dev/null +++ b/SubsonicMono/SubsonicAPI/AssemblyInfo.cs @@ -0,0 +1,27 @@ +using System.Reflection; +using System.Runtime.CompilerServices; + +// Information about this assembly is defined by the following attributes. +// Change them to the values specific to your project. + +[assembly: AssemblyTitle("SubsonicAPI")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("")] +[assembly: AssemblyCopyright("")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". +// The form "{Major}.{Minor}.*" will automatically update the build and revision, +// and "{Major}.{Minor}.{Build}.*" will update just the revision. + +[assembly: AssemblyVersion("1.0.*")] + +// The following attributes are used to specify the signing key for the assembly, +// if desired. See the Mono documentation for more information about signing. + +//[assembly: AssemblyDelaySign(false)] +//[assembly: AssemblyKeyFile("")] + diff --git a/SubsonicMono/SubsonicAPI/SubsonicAPI.cs b/SubsonicMono/SubsonicAPI/SubsonicAPI.cs new file mode 100644 index 0000000..3632e93 --- /dev/null +++ b/SubsonicMono/SubsonicAPI/SubsonicAPI.cs @@ -0,0 +1,648 @@ +/************************************************************************** + Subsonic Csharp + Copyright (C) 2010 Ian Fijolek + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +**************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Text; +using System.Net; +using System.IO; +using System.Xml; + +namespace SubsonicAPI +{ + #region Classes + + public class SubsonicItem + { + public enum SubsonicItemType + { + Folder, Song, Artist, Library + } + + public string name; + public string id; + public string lastModified; + public string lastAccessed; + public SubsonicItemType itemType; + public SubsonicItem parent; + private List _children; + + public List children + { + get + { + if (_children == null) + { + if (this.itemType == SubsonicItemType.Song) + _children = null; + else + _children = Subsonic.GetItemChildren(this, ""); + } + return _children; + } + set + { + _children = value; + } + } + + public SubsonicItem() + { + this.name = ""; + this.id = ""; + this.lastAccessed = DateTime.Now.ToString(); + } + + public SubsonicItem(string name, string id) + { + this.name = name; + this.id = name; + this.lastAccessed = DateTime.Now.ToString(); + } + + public SubsonicItem(string name, string id, SubsonicItemType itemType, SubsonicItem parent) + { + this.name = name; + this.id = id; + this.itemType = itemType; + this.parent = parent; + this.lastAccessed = DateTime.Now.ToString(); + } + + public override string ToString() + { + return name; + } + + public SubsonicItem FindItemById(string id) + { + SubsonicItem foundItem = null; + + // If the current item is the item we are looking for, return it + if (this.id == id) + foundItem = this; + // Otherwise, we check the children if they exist + else if (_children != null) + { + foreach(SubsonicItem child in _children) + { + // If this child is the item we are looking for, return it + if (child.id == id) + { + foundItem = child; + break; + } + else + { + foundItem = child.FindItemById(id); + if (foundItem != null) + break; + } + } + } + + return foundItem; + } + + public SubsonicItem GetChildByName(string childName) + { + SubsonicItem theItem = null; + + if (_children != null) + { + theItem = _children.Find( + delegate(SubsonicItem itm) + { + return itm.name == childName ; + } + ); + } + + return theItem; + } + } + + public class Song : SubsonicItem + { + public string artist; + public string album; + public string title; + + public Song() + { + this.artist = ""; + this.title = ""; + this.album = ""; + this.name = ""; + this.id = ""; + this.itemType = SubsonicItem.SubsonicItemType.Song; + this.parent = null; + this.lastAccessed = DateTime.Now.ToString(); + } + + public Song(string title,string artist, string album, string id) + { + this.artist = artist; + this.title = title; + this.album = album; + this.name = title; + this.id = id; + this.itemType = SubsonicItem.SubsonicItemType.Song; + this.parent = null; + this.lastAccessed = DateTime.Now.ToString(); + } + + public Song(string title, string artist, string album, string id, SubsonicItem parent) + { + this.artist = artist; + this.title = title; + this.album = album; + this.name = title; + this.id = id; + this.itemType = SubsonicItem.SubsonicItemType.Song; + this.parent = parent; + this.lastAccessed = DateTime.Now.ToString(); + } + + public Stream getStream() + { + return Subsonic.StreamSong(this.id); + } + + public override string ToString() + { + return artist + " - " + title; + } + } + + #endregion Classes + + /// + /// Open Source C# Implementation of the Subsonic API + /// http://www.subsonic.org/pages/api.jsp + /// + public static class Subsonic + { + private static SubsonicItem _MyLibrary; + + /// + /// Public Property that can be used for auto-retrieving children + /// + public static SubsonicItem MyLibrary + { + get + { + return _MyLibrary; + } + set + { + _MyLibrary = value; + } + } + + // Should be set from application layer when the application is loaded + public static string appName; + + // Min version of the REST API implemented + private static string apiVersion = "1.3.0"; + + // Set with the login method + static string server; + static string authHeader; + + // Used for generating direct URLS + static string encPass; + static string username; + + /// + /// Takes parameters for server, username and password to generate an auth header + /// and Pings the server + /// + /// + /// + /// + /// Resulting XML (Future boolean) + public static string LogIn(string theServer, string user, string password) + { + string result = "Nothing Happened"; + + server = theServer; + authHeader = user + ":" + password; + authHeader = Convert.ToBase64String(Encoding.Default.GetBytes(authHeader)); + + // Store user and encoded password for alternate authentication + username = user; + Byte[] passwordBytes = Encoding.Default.GetBytes(password); + for (int i = 0; i < passwordBytes.Length; i++) + encPass += passwordBytes[i].ToString("x2"); + + Stream theStream = MakeGenericRequest("ping", null); + + StreamReader sr = new StreamReader(theStream); + + result = sr.ReadToEnd(); + + /// TODO: Parse the result and determine if logged in or not + + _MyLibrary = new SubsonicItem("LibraryRoot", "-1", SubsonicItem.SubsonicItemType.Library, null); + + return result; + } + + /// + /// Uses the Auth Header for logged in user to make an HTTP request to the server + /// with the given Subsonic API method and parameters + /// + /// + /// + /// Datastream of the server response + public static Stream MakeGenericRequest(string method, Dictionary parameters) + { + // Check to see if Logged In yet + if (string.IsNullOrEmpty(authHeader)) + { + // Throw a Not Logged In exception + Exception e = new Exception("No Authorization header. Must Log In first"); + return null; + } + else + { + if (!method.EndsWith(".view")) + method += ".view"; + + string requestURL = BuildRequestURL(method, parameters); + + WebRequest theRequest = WebRequest.Create(requestURL); + theRequest.Method = "GET"; + + theRequest.Headers["Authorization"] = "Basic " + authHeader; + + WebResponse response = theRequest.GetResponse(); + + Stream dataStream = response.GetResponseStream(); + + return dataStream; + } + } + + /// + /// Creates a URL for a request but does not make the actual request using set login credentials an dmethod and parameters + /// + /// + /// + /// Proper Subsonic API URL for a request + public static string BuildRequestURL(string method, Dictionary parameters) + { + string requestURL = "http://" + server + "/rest/" + method + "?v=" + apiVersion + "&c=" + appName; + if (parameters != null) + { + foreach (KeyValuePair parameter in parameters) + { + requestURL += "&" + parameter.Key + "=" + parameter.Value; + } + } + return requestURL; + } + + /// + /// Creates a URL for a command with username and encoded pass in the URL + /// + /// + /// + /// URL for streaming a song or retrieving the results of a call + public static string BuildDirectURL(string method, Dictionary parameters) + { + string callURL = "http://" + server + "/rest/" + method + "?v=" + apiVersion + "&c=" + appName + + "&u=" + username + "&p=enc:" + encPass; + if (parameters != null) + { + foreach (KeyValuePair parameter in parameters) + { + callURL += "&" + parameter.Key + "=" + parameter.Value; + } + } + return callURL; + } + + /// + /// Returns a list of SubsonicItems that fall inside the parent object + /// + /// + /// A + /// + /// + /// A + /// + /// + /// A + /// + public static List GetItemChildren(SubsonicItem parent, string ifModifiedSince) + { + Dictionary parameters = new Dictionary(); + + // Generate the proper request for the parent type + string requestType, musicFolderId; + if (parent.itemType == SubsonicItem.SubsonicItemType.Library) + { + requestType = "getIndexes"; + if (parent.id != "-1") + parameters.Add("musicFolderId", parent.id); + } + else + { + requestType = "getMusicDirectory"; + parameters.Add("id", parent.id); + } + + // Load the parameters if provided + if (!string.IsNullOrEmpty(ifModifiedSince)) + parameters.Add("ifModifiedSince", ifModifiedSince); + + // Make the request + Stream theStream = MakeGenericRequest(requestType, parameters); + // Read the response as a string + StreamReader sr = new StreamReader(theStream); + string result = sr.ReadToEnd(); + + // Parse the resulting XML string into an XmlDocument + XmlDocument myXML = new XmlDocument(); + myXML.LoadXml(result); + + List children = new List(); + + // Parse the artist + if (parent.itemType == SubsonicItem.SubsonicItemType.Library) + { + if (myXML.ChildNodes[1].Name == "subsonic-response") + { + if (myXML.ChildNodes[1].FirstChild.Name == "indexes") + { + for (int i = 0; i < myXML.ChildNodes[1].FirstChild.ChildNodes.Count; i++) + { + for (int j = 0; j < myXML.ChildNodes[1].FirstChild.ChildNodes[i].ChildNodes.Count; j++) + { + string artist = myXML.ChildNodes[1].FirstChild.ChildNodes[i].ChildNodes[j].Attributes["name"].Value; + string id = myXML.ChildNodes[1].FirstChild.ChildNodes[i].ChildNodes[j].Attributes["id"].Value; + + children.Add(new SubsonicItem(artist, id, SubsonicItem.SubsonicItemType.Folder, parent)); + } + } + } + } + } + // Parse the directory + else if (parent.itemType == SubsonicItem.SubsonicItemType.Folder) + { + if (myXML.ChildNodes[1].Name == "subsonic-response") + { + if (myXML.ChildNodes[1].FirstChild.Name == "directory") + { + for (int i = 0; i < myXML.ChildNodes[1].FirstChild.ChildNodes.Count; i++) + { + bool isDir = bool.Parse(myXML.ChildNodes[1].FirstChild.ChildNodes[i].Attributes["isDir"].Value); + string title = myXML.ChildNodes[1].FirstChild.ChildNodes[i].Attributes["title"].Value; + string id = myXML.ChildNodes[1].FirstChild.ChildNodes[i].Attributes["id"].Value; + + SubsonicItem theItem = new SubsonicItem(title, id, (isDir ? SubsonicItem.SubsonicItemType.Folder : SubsonicItem.SubsonicItemType.Song), parent); + children.Add(theItem); + } + } + } + } + + return children; + } + + /// + /// Returns an indexed structure of all artists. + /// + /// Required: No; If specified, only return artists in the music folder with the given ID. + /// Required: No; If specified, only return a result if the artist collection has changed since the given time. + /// Dictionary, Key = Artist and Value = id + public static List GetIndexes(string musicFolderId, string ifModifiedSince) + { + // Load the parameters if provided + Dictionary parameters = new Dictionary(); + if (!string.IsNullOrEmpty(musicFolderId)) + parameters.Add("musicFolderId", musicFolderId); + + if (!string.IsNullOrEmpty(ifModifiedSince)) + parameters.Add("ifModifiedSince", ifModifiedSince); + + // Make the request + Stream theStream = MakeGenericRequest("getIndexes", parameters); + // Read the response as a string + StreamReader sr = new StreamReader(theStream); + string result = sr.ReadToEnd(); + + // Parse the resulting XML string into an XmlDocument + XmlDocument myXML = new XmlDocument(); + myXML.LoadXml(result); + + // Parse the XML document into a List + List artists = new List(); + if (myXML.ChildNodes[1].Name == "subsonic-response") + { + if (myXML.ChildNodes[1].FirstChild.Name == "indexes") + { + int i = 0; + for (i = 0; i < myXML.ChildNodes[1].FirstChild.ChildNodes.Count; i++) + { + int j = 0; + for (j = 0; j < myXML.ChildNodes[1].FirstChild.ChildNodes[i].ChildNodes.Count; j++) + { + string artist = myXML.ChildNodes[1].FirstChild.ChildNodes[i].ChildNodes[j].Attributes["name"].Value; + string id = myXML.ChildNodes[1].FirstChild.ChildNodes[i].ChildNodes[j].Attributes["id"].Value; + + artists.Add(new SubsonicItem(artist, id)); + } + } + } + } + + return artists; + } + + public static List GetIndexes(string musicFolderId) + { + return GetIndexes(musicFolderId, ""); + } + + public static List GetIndexes() + { + return GetIndexes("", ""); + } + + /// + /// Streams a given music file. (Renamed from request name "stream") + /// + /// Required: Yes; A string which uniquely identifies the file to stream. + /// Obtained by calls to getMusicDirectory. + /// Required: No; If specified, the server will attempt to + /// limit the bitrate to this value, in kilobits per second. If set to zero, no limit + /// is imposed. Legal values are: 0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256 and 320. + /// + public static Stream StreamSong(string id, int? maxBitRate) + { + // Reades the id of the song and sets it as a parameter + Dictionary theParameters = new Dictionary(); + theParameters.Add("id", id); + if (maxBitRate.HasValue) + theParameters.Add("maxBitRate", maxBitRate.ToString()); + + // Makes the request + Stream theStream = MakeGenericRequest("stream", theParameters); + + return theStream; + } + + public static Stream StreamSong(string id) + { + return StreamSong(id, null); + } + + /// + /// Returns a listing of all files in a music directory. Typically used to get list of albums for an artist, or list of songs for an album. + /// + /// A string which uniquely identifies the music folder. Obtained by calls to getIndexes or getMusicDirectory. + /// MusicFolder object containing info for the specified directory + public static List GetMusicDirectory(string id) + { + Dictionary theParameters = new Dictionary(); + theParameters.Add("id", id); + Stream theStream = MakeGenericRequest("getMusicDirectory", theParameters); + + StreamReader sr = new StreamReader(theStream); + + string result = sr.ReadToEnd(); + + XmlDocument myXML = new XmlDocument(); + myXML.LoadXml(result); + + List theContents = new List(); + + if (myXML.ChildNodes[1].Name == "subsonic-response") + { + if (myXML.ChildNodes[1].FirstChild.Name == "directory") + { + SubsonicItem theParent = new SubsonicItem(); + theParent.name = myXML.ChildNodes[1].FirstChild.Attributes["name"].Value; + theParent.id = myXML.ChildNodes[1].FirstChild.Attributes["id"].Value; + + int i = 0; + for (i = 0; i < myXML.ChildNodes[1].FirstChild.ChildNodes.Count; i++) + { + bool isDir = bool.Parse(myXML.ChildNodes[1].FirstChild.ChildNodes[i].Attributes["isDir"].Value); + string title = myXML.ChildNodes[1].FirstChild.ChildNodes[i].Attributes["title"].Value; + string theId = myXML.ChildNodes[1].FirstChild.ChildNodes[i].Attributes["id"].Value; + + SubsonicItem theItem = new SubsonicItem(title, theId, (isDir ? SubsonicItem.SubsonicItemType.Folder : SubsonicItem.SubsonicItemType.Song), theParent); + theContents.Add(theItem); + } + } + } + + return theContents; + } + + /// + /// Returns what is currently being played by all users. Takes no extra parameters. + /// + public static List GetNowPlaying() + { + List nowPlaying = new List(); + + Dictionary theParameters = new Dictionary(); + Stream theStream = MakeGenericRequest("getNowPlaying", theParameters); + StreamReader sr = new StreamReader(theStream); + string result = sr.ReadToEnd(); + + + return nowPlaying; + } + + /// + /// Performs a search valid for the current version of the subsonic server + /// + /// The Term you want to search for + /// A List of SubsonicItem objects + public static List Search(string query) + { + Dictionary parameters = new Dictionary(); + Version apiV = new Version(apiVersion); + Version Search2Min = new Version("1.4.0"); + string request = ""; + if (apiV >= Search2Min) + { + request = "search2"; + parameters.Add("query", query); + } + else + { + request = "search"; + parameters.Add("any", query); + } + + // Make the request + Stream theStream = MakeGenericRequest(request, parameters); + // Read the response as a string + StreamReader sr = new StreamReader(theStream); + string result = sr.ReadToEnd(); + + // Parse the resulting XML string into an XmlDocument + XmlDocument myXML = new XmlDocument(); + myXML.LoadXml(result); + + List searchResults = new List(); + + // Parse the artist + if (myXML.ChildNodes[1].Name == "subsonic-response") + { + if (myXML.ChildNodes[1].FirstChild.Name == "searchResult") + { + for (int i = 0; i < myXML.ChildNodes[1].FirstChild.ChildNodes.Count; i++) + { + bool isDir = bool.Parse(myXML.ChildNodes[1].FirstChild.ChildNodes[i].Attributes["isDir"].Value); + string title = myXML.ChildNodes[1].FirstChild.ChildNodes[i].Attributes["title"].Value; + string theId = myXML.ChildNodes[1].FirstChild.ChildNodes[i].Attributes["id"].Value; + string artist = ""; + string album = ""; + + if (!isDir) + { + artist = myXML.ChildNodes[1].FirstChild.ChildNodes[i].Attributes["artist"].Value; + album = myXML.ChildNodes[1].FirstChild.ChildNodes[i].Attributes["album"].Value; + } + + SubsonicItem theItem; + if (isDir) + theItem = new SubsonicItem(title, theId, SubsonicItem.SubsonicItemType.Folder, null); + else + theItem = new Song(title, artist, album, theId); + + searchResults.Add(theItem); + } + } + } + + return searchResults; + } + + } + +} \ No newline at end of file diff --git a/SubsonicMono/SubsonicAPI/SubsonicAPI.csproj b/SubsonicMono/SubsonicAPI/SubsonicAPI.csproj new file mode 100644 index 0000000..72e7d60 --- /dev/null +++ b/SubsonicMono/SubsonicAPI/SubsonicAPI.csproj @@ -0,0 +1,41 @@ + + + + Debug + AnyCPU + 9.0.21022 + 2.0 + {AD32EC84-3CE8-4E20-934E-6B4F40A9BC08} + Library + SubsonicAPI + SubsonicAPI + v3.5 + + + true + full + false + bin\Debug + DEBUG + prompt + 4 + false + + + none + false + bin\Release + prompt + 4 + false + + + + + + + + + + + \ No newline at end of file diff --git a/SubsonicMono/SubsonicAPI/SubsonicAPI.pidb b/SubsonicMono/SubsonicAPI/SubsonicAPI.pidb new file mode 100644 index 0000000..1e55b11 Binary files /dev/null and b/SubsonicMono/SubsonicAPI/SubsonicAPI.pidb differ diff --git a/SubsonicMono/SubsonicMono.sln b/SubsonicMono/SubsonicMono.sln new file mode 100644 index 0000000..58adc8f --- /dev/null +++ b/SubsonicMono/SubsonicMono.sln @@ -0,0 +1,32 @@ + +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual Studio 2008 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SubsonicPlayer", "SubsonicMono\SubsonicPlayer.csproj", "{416D955F-4CD9-41C8-BB9D-BF17B59710B8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SubsonicAPI", "SubsonicAPI\SubsonicAPI.csproj", "{AD32EC84-3CE8-4E20-934E-6B4F40A9BC08}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SubsonicWeb", "SubsonicWeb\SubsonicWeb.csproj", "{91BB17F9-5F24-4329-BA99-8A96B800416E}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x86 = Debug|x86 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {416D955F-4CD9-41C8-BB9D-BF17B59710B8}.Debug|x86.ActiveCfg = Debug|x86 + {416D955F-4CD9-41C8-BB9D-BF17B59710B8}.Debug|x86.Build.0 = Debug|x86 + {416D955F-4CD9-41C8-BB9D-BF17B59710B8}.Release|x86.ActiveCfg = Release|x86 + {416D955F-4CD9-41C8-BB9D-BF17B59710B8}.Release|x86.Build.0 = Release|x86 + {91BB17F9-5F24-4329-BA99-8A96B800416E}.Debug|x86.ActiveCfg = Debug|Any CPU + {91BB17F9-5F24-4329-BA99-8A96B800416E}.Debug|x86.Build.0 = Debug|Any CPU + {91BB17F9-5F24-4329-BA99-8A96B800416E}.Release|x86.ActiveCfg = Release|Any CPU + {91BB17F9-5F24-4329-BA99-8A96B800416E}.Release|x86.Build.0 = Release|Any CPU + {AD32EC84-3CE8-4E20-934E-6B4F40A9BC08}.Debug|x86.ActiveCfg = Debug|Any CPU + {AD32EC84-3CE8-4E20-934E-6B4F40A9BC08}.Debug|x86.Build.0 = Debug|Any CPU + {AD32EC84-3CE8-4E20-934E-6B4F40A9BC08}.Release|x86.ActiveCfg = Release|Any CPU + {AD32EC84-3CE8-4E20-934E-6B4F40A9BC08}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(MonoDevelopProperties) = preSolution + StartupItem = SubsonicMono\SubsonicPlayer.csproj + EndGlobalSection +EndGlobal diff --git a/SubsonicMono/SubsonicMono.userprefs b/SubsonicMono/SubsonicMono.userprefs new file mode 100644 index 0000000..6f99dbf --- /dev/null +++ b/SubsonicMono/SubsonicMono.userprefs @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/SubsonicMono/SubsonicMono/AssemblyInfo.cs b/SubsonicMono/SubsonicMono/AssemblyInfo.cs new file mode 100644 index 0000000..f2010c3 --- /dev/null +++ b/SubsonicMono/SubsonicMono/AssemblyInfo.cs @@ -0,0 +1,27 @@ +using System.Reflection; +using System.Runtime.CompilerServices; + +// Information about this assembly is defined by the following attributes. +// Change them to the values specific to your project. + +[assembly: AssemblyTitle("SubsonicMono")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("")] +[assembly: AssemblyCopyright("")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". +// The form "{Major}.{Minor}.*" will automatically update the build and revision, +// and "{Major}.{Minor}.{Build}.*" will update just the revision. + +[assembly: AssemblyVersion("1.0.*")] + +// The following attributes are used to specify the signing key for the assembly, +// if desired. See the Mono documentation for more information about signing. + +//[assembly: AssemblyDelaySign(false)] +//[assembly: AssemblyKeyFile("")] + diff --git a/SubsonicMono/SubsonicMono/ItemLoad.gif b/SubsonicMono/SubsonicMono/ItemLoad.gif new file mode 100644 index 0000000..5b33f7e Binary files /dev/null and b/SubsonicMono/SubsonicMono/ItemLoad.gif differ diff --git a/SubsonicMono/SubsonicMono/Main.cs b/SubsonicMono/SubsonicMono/Main.cs new file mode 100644 index 0000000..4ab4bc5 --- /dev/null +++ b/SubsonicMono/SubsonicMono/Main.cs @@ -0,0 +1,17 @@ +using System; +using Gtk; + +namespace SubsonicMono +{ + class MainClass + { + public static void Main (string[] args) + { + Application.Init (); + MainWindow win = new MainWindow (); + win.Show (); + Application.Run (); + } + } +} + diff --git a/SubsonicMono/SubsonicMono/MainWindow.cs b/SubsonicMono/SubsonicMono/MainWindow.cs new file mode 100644 index 0000000..b8ae23d --- /dev/null +++ b/SubsonicMono/SubsonicMono/MainWindow.cs @@ -0,0 +1,203 @@ +/************************************************************************** + Subsonic Csharp + Copyright (C) 2010 Ian Fijolek + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +**************************************************************************/ + +using System; +using Gtk; +using SubsonicAPI; +using System.Collections.Generic; +using HollyLibrary; +using System.Threading; + +public partial class MainWindow : Gtk.Window +{ + HTreeView tvLibrary; + HSimpleList slPlaylist; + + public MainWindow () : base(Gtk.WindowType.Toplevel) + { + Build (); + Subsonic.appName = "IansCsharpApp"; + + InitializeTreeView(); + InitializePlaylist(); + } + + private void InitializeTreeView() + { + tvLibrary = new HTreeView(); + swLibrary.Add(tvLibrary); + tvLibrary.NodeExpanded += tvLibraryNodeExpanded; + tvLibrary.Editable = false; + tvLibrary.Visible = true; + } + + private void InitializePlaylist() + { + slPlaylist = new HSimpleList(); + swPlayQueue.Add(slPlaylist); + slPlaylist.Visible = true; + } + + void tvLibraryNodeExpanded (object sender, NodeEventArgs args) + { + // Fetch any items inside the node... + HTreeNode thisNode = args.Node; + + // Check to see if it has any children + if (thisNode.Nodes.Count == 1 && thisNode.Nodes[0].Text == "") + { + // Node child is a dummy + thisNode.Nodes[0].Text = "Loading..."; + + // Get path to the selected node to expandsimp + Queue nodePath = GetNodePath(thisNode); + + // Dive into library to selected node + SubsonicItem thisItem = Subsonic.MyLibrary; + while (nodePath.Count > 0) + { + thisItem = thisItem.GetChildByName(nodePath.Dequeue()); + } + + // Should now have the correct selected item + foreach(SubsonicItem child in thisItem.children) + { + HTreeNode childNode = new HTreeNode(child.name); + thisNode.Nodes.Add(childNode); + + // Adding a dummy node for any Folders + if (child.itemType == SubsonicItem.SubsonicItemType.Folder) + childNode.Nodes.Add(new HTreeNode("")); + } + + // Remove dummy node + thisNode.Nodes.RemoveAt(0); + } + } + + private Queue GetNodePath(HTreeNode theNode) + { + // Create a queue that will hold the name of all parent objects + Queue nodePath; + if (theNode.ParentNode != null) + { + // If this node has a parent, then recurse + nodePath = GetNodePath(theNode.ParentNode); + } + else + { + // If the praent, then initialize the path + nodePath = new Queue(); + } + + // Add enqueue this item in the path + nodePath.Enqueue(theNode.Text); + + return nodePath; + } + + private SubsonicItem GetNodeItem(HTreeNode theNode) + { + // Get path to the selected node + Queue nodePath = GetNodePath(theNode); + + // Dive into library to selected node + SubsonicItem thisItem = Subsonic.MyLibrary; + while (nodePath.Count > 0) + { + thisItem = thisItem.GetChildByName(nodePath.Dequeue()); + } + + return thisItem; + } + + protected void OnDeleteEvent (object sender, DeleteEventArgs a) + { + Application.Quit (); + a.RetVal = true; + } + + protected virtual void OnBtnLogin2Clicked (object sender, System.EventArgs e) + { + string server = tbServer.Text; + string user = tbUsername.Text; + string passw0rdd = tbPaassw0rd.Text; + + string loginResult = Subsonic.LogIn(server, user, passw0rdd); + Console.WriteLine("Login Result: " + loginResult); + + SubsonicItem thisLibrary = Subsonic.MyLibrary; + foreach(SubsonicItem artist in thisLibrary.children) + { + HTreeNode artistNode = new HTreeNode(artist.name); + tvLibrary.Nodes.Add(artistNode); + + // Adding a dummy node for the artist + artistNode.Nodes.Add(new HTreeNode("")); + } + } + + protected virtual void OnBtnQueueSongClicked (object sender, System.EventArgs e) + { + HTreeNode theNode = tvLibrary.SelectedNode; + + // Check if node has children + if (theNode.Nodes.Count > 0) + { + // Will add all children to queue + } + else + { + // Node is a leaf (song) + SubsonicItem theItem = GetNodeItem(theNode); + + // Confirm that the item is asong + if (theItem.itemType == SubsonicItem.SubsonicItemType.Song) + { + //slPlaylist.Items.Add(theItem); + + Dictionary songId = new Dictionary(); + songId.Add("id", theItem.id); + string streamURL = Subsonic.BuildDirectURL("download.view", songId); + + System.Diagnostics.Process proc = new System.Diagnostics.Process(); + proc.StartInfo.FileName = "vlc"; + proc.StartInfo.Arguments = "--one-instance --playlist-enqueue " + streamURL; + proc.Start(); + } + + } + } + + protected virtual void OnBtnSearchClicked (object sender, System.EventArgs e) + { + string search = tbSearch.Text; + + List results = Subsonic.Search(search); + + foreach (SubsonicItem si in results) + slPlaylist.Items.Add(si); + } + + + + + + +} + diff --git a/SubsonicMono/SubsonicMono/README b/SubsonicMono/SubsonicMono/README new file mode 100644 index 0000000..350451f --- /dev/null +++ b/SubsonicMono/SubsonicMono/README @@ -0,0 +1,9 @@ +Application: Subsonic Csharp +Author: Ian Fijolek +License: GPLv3 +Homepage: http://code.google.com/p/subsonic-csharp/ + +Please refer to the home page for license information as well +as source code, documentation and compiling instructions + +Uses HollyLibrary.dll (LGPL) located at http://code.google.com/p/holly-gtk-widgets/ diff --git a/SubsonicMono/SubsonicMono/SubsonicPlayer.csproj b/SubsonicMono/SubsonicMono/SubsonicPlayer.csproj new file mode 100644 index 0000000..6630a7c --- /dev/null +++ b/SubsonicMono/SubsonicMono/SubsonicPlayer.csproj @@ -0,0 +1,85 @@ + + + + Debug + x86 + 9.0.21022 + 2.0 + {416D955F-4CD9-41C8-BB9D-BF17B59710B8} + WinExe + SubsonicMono + SubsonicMono + v3.5 + + + true + full + false + bin\Debug + DEBUG + prompt + 4 + x86 + false + + + none + false + bin\Release + prompt + 4 + x86 + false + + + + + False + gtk-sharp-2.0 + + + False + gtk-sharp-2.0 + + + False + glib-sharp-2.0 + + + False + glade-sharp-2.0 + + + False + gtk-sharp-2.0 + + + False + gtk-sharp-2.0 + + + + False + ..\..\..\MonoLibs\HollyLibrary.dll + + + + + gui.stetic + + + + + + + + + + + + + {AD32EC84-3CE8-4E20-934E-6B4F40A9BC08} + SubsonicAPI + + + \ No newline at end of file diff --git a/SubsonicMono/SubsonicMono/SubsonicPlayer.pidb b/SubsonicMono/SubsonicMono/SubsonicPlayer.pidb new file mode 100644 index 0000000..52bd816 Binary files /dev/null and b/SubsonicMono/SubsonicMono/SubsonicPlayer.pidb differ diff --git a/SubsonicMono/SubsonicMono/gtk-gui/MainWindow.cs b/SubsonicMono/SubsonicMono/gtk-gui/MainWindow.cs new file mode 100644 index 0000000..bd81428 --- /dev/null +++ b/SubsonicMono/SubsonicMono/gtk-gui/MainWindow.cs @@ -0,0 +1,192 @@ + +// This file has been generated by the GUI designer. Do not modify. + +public partial class MainWindow +{ + private global::Gtk.Fixed frMain; + + private global::Gtk.Label lblTitle; + + private global::Gtk.Entry tbServer; + + private global::Gtk.Label lbServer; + + private global::Gtk.Label lblUsername; + + private global::Gtk.Entry tbUsername; + + private global::Gtk.Label lbPassw00rd; + + private global::Gtk.Entry tbPaassw0rd; + + private global::Gtk.ScrolledWindow swLibrary; + + private global::Gtk.Button btnLogin2; + + private global::Gtk.ScrolledWindow swPlayQueue; + + private global::Gtk.Button btnQueueSong; + + private global::Gtk.Button btnSearch; + + private global::Gtk.Entry tbSearch; + + private global::Gtk.Label label1; + + protected virtual void Build () + { + global::Stetic.Gui.Initialize (this); + // Widget MainWindow + this.Name = "MainWindow"; + this.Title = global::Mono.Unix.Catalog.GetString ("MainWindow"); + this.WindowPosition = ((global::Gtk.WindowPosition)(4)); + // Container child MainWindow.Gtk.Container+ContainerChild + this.frMain = new global::Gtk.Fixed (); + this.frMain.Name = "frMain"; + this.frMain.HasWindow = false; + // Container child frMain.Gtk.Fixed+FixedChild + this.lblTitle = new global::Gtk.Label (); + this.lblTitle.Name = "lblTitle"; + this.lblTitle.LabelProp = global::Mono.Unix.Catalog.GetString ("Subsonic"); + this.frMain.Add (this.lblTitle); + global::Gtk.Fixed.FixedChild w1 = ((global::Gtk.Fixed.FixedChild)(this.frMain[this.lblTitle])); + w1.X = 9; + w1.Y = 10; + // Container child frMain.Gtk.Fixed+FixedChild + this.tbServer = new global::Gtk.Entry (); + this.tbServer.CanFocus = true; + this.tbServer.Name = "tbServer"; + this.tbServer.IsEditable = true; + this.tbServer.InvisibleChar = '•'; + this.frMain.Add (this.tbServer); + global::Gtk.Fixed.FixedChild w2 = ((global::Gtk.Fixed.FixedChild)(this.frMain[this.tbServer])); + w2.X = 9; + w2.Y = 71; + // Container child frMain.Gtk.Fixed+FixedChild + this.lbServer = new global::Gtk.Label (); + this.lbServer.Name = "lbServer"; + this.lbServer.LabelProp = global::Mono.Unix.Catalog.GetString ("Server URL:"); + this.frMain.Add (this.lbServer); + global::Gtk.Fixed.FixedChild w3 = ((global::Gtk.Fixed.FixedChild)(this.frMain[this.lbServer])); + w3.X = 13; + w3.Y = 46; + // Container child frMain.Gtk.Fixed+FixedChild + this.lblUsername = new global::Gtk.Label (); + this.lblUsername.Name = "lblUsername"; + this.lblUsername.LabelProp = global::Mono.Unix.Catalog.GetString ("Username:"); + this.frMain.Add (this.lblUsername); + global::Gtk.Fixed.FixedChild w4 = ((global::Gtk.Fixed.FixedChild)(this.frMain[this.lblUsername])); + w4.X = 15; + w4.Y = 110; + // Container child frMain.Gtk.Fixed+FixedChild + this.tbUsername = new global::Gtk.Entry (); + this.tbUsername.CanFocus = true; + this.tbUsername.Name = "tbUsername"; + this.tbUsername.IsEditable = true; + this.tbUsername.InvisibleChar = '•'; + this.frMain.Add (this.tbUsername); + global::Gtk.Fixed.FixedChild w5 = ((global::Gtk.Fixed.FixedChild)(this.frMain[this.tbUsername])); + w5.X = 11; + w5.Y = 138; + // Container child frMain.Gtk.Fixed+FixedChild + this.lbPassw00rd = new global::Gtk.Label (); + this.lbPassw00rd.Name = "lbPassw00rd"; + this.lbPassw00rd.LabelProp = global::Mono.Unix.Catalog.GetString ("Password:"); + this.frMain.Add (this.lbPassw00rd); + global::Gtk.Fixed.FixedChild w6 = ((global::Gtk.Fixed.FixedChild)(this.frMain[this.lbPassw00rd])); + w6.X = 13; + w6.Y = 176; + // Container child frMain.Gtk.Fixed+FixedChild + this.tbPaassw0rd = new global::Gtk.Entry (); + this.tbPaassw0rd.CanFocus = true; + this.tbPaassw0rd.Name = "tbPaassw0rd"; + this.tbPaassw0rd.IsEditable = true; + this.tbPaassw0rd.Visibility = false; + this.tbPaassw0rd.InvisibleChar = '•'; + this.frMain.Add (this.tbPaassw0rd); + global::Gtk.Fixed.FixedChild w7 = ((global::Gtk.Fixed.FixedChild)(this.frMain[this.tbPaassw0rd])); + w7.X = 13; + w7.Y = 199; + // Container child frMain.Gtk.Fixed+FixedChild + this.swLibrary = new global::Gtk.ScrolledWindow (); + this.swLibrary.WidthRequest = 262; + this.swLibrary.HeightRequest = 348; + this.swLibrary.CanFocus = true; + this.swLibrary.Name = "swLibrary"; + this.swLibrary.ShadowType = ((global::Gtk.ShadowType)(1)); + this.frMain.Add (this.swLibrary); + global::Gtk.Fixed.FixedChild w8 = ((global::Gtk.Fixed.FixedChild)(this.frMain[this.swLibrary])); + w8.X = 10; + w8.Y = 295; + // Container child frMain.Gtk.Fixed+FixedChild + this.btnLogin2 = new global::Gtk.Button (); + this.btnLogin2.CanFocus = true; + this.btnLogin2.Name = "btnLogin2"; + this.btnLogin2.UseUnderline = true; + this.btnLogin2.Label = global::Mono.Unix.Catalog.GetString ("Login2"); + this.frMain.Add (this.btnLogin2); + global::Gtk.Fixed.FixedChild w9 = ((global::Gtk.Fixed.FixedChild)(this.frMain[this.btnLogin2])); + w9.X = 14; + w9.Y = 241; + // Container child frMain.Gtk.Fixed+FixedChild + this.swPlayQueue = new global::Gtk.ScrolledWindow (); + this.swPlayQueue.WidthRequest = 208; + this.swPlayQueue.HeightRequest = 338; + this.swPlayQueue.CanFocus = true; + this.swPlayQueue.Name = "swPlayQueue"; + this.swPlayQueue.ShadowType = ((global::Gtk.ShadowType)(1)); + this.frMain.Add (this.swPlayQueue); + global::Gtk.Fixed.FixedChild w10 = ((global::Gtk.Fixed.FixedChild)(this.frMain[this.swPlayQueue])); + w10.X = 315; + w10.Y = 296; + // Container child frMain.Gtk.Fixed+FixedChild + this.btnQueueSong = new global::Gtk.Button (); + this.btnQueueSong.CanFocus = true; + this.btnQueueSong.Name = "btnQueueSong"; + this.btnQueueSong.UseUnderline = true; + this.btnQueueSong.Label = global::Mono.Unix.Catalog.GetString (">"); + this.frMain.Add (this.btnQueueSong); + global::Gtk.Fixed.FixedChild w11 = ((global::Gtk.Fixed.FixedChild)(this.frMain[this.btnQueueSong])); + w11.X = 280; + w11.Y = 425; + // Container child frMain.Gtk.Fixed+FixedChild + this.btnSearch = new global::Gtk.Button (); + this.btnSearch.CanFocus = true; + this.btnSearch.Name = "btnSearch"; + this.btnSearch.UseUnderline = true; + this.btnSearch.Label = global::Mono.Unix.Catalog.GetString ("Search"); + this.frMain.Add (this.btnSearch); + global::Gtk.Fixed.FixedChild w12 = ((global::Gtk.Fixed.FixedChild)(this.frMain[this.btnSearch])); + w12.X = 323; + w12.Y = 247; + // Container child frMain.Gtk.Fixed+FixedChild + this.tbSearch = new global::Gtk.Entry (); + this.tbSearch.CanFocus = true; + this.tbSearch.Name = "tbSearch"; + this.tbSearch.IsEditable = true; + this.tbSearch.InvisibleChar = '•'; + this.frMain.Add (this.tbSearch); + global::Gtk.Fixed.FixedChild w13 = ((global::Gtk.Fixed.FixedChild)(this.frMain[this.tbSearch])); + w13.X = 315; + w13.Y = 213; + // Container child frMain.Gtk.Fixed+FixedChild + this.label1 = new global::Gtk.Label (); + this.label1.Name = "label1"; + this.label1.LabelProp = global::Mono.Unix.Catalog.GetString ("Search"); + this.frMain.Add (this.label1); + global::Gtk.Fixed.FixedChild w14 = ((global::Gtk.Fixed.FixedChild)(this.frMain[this.label1])); + w14.X = 318; + w14.Y = 179; + this.Add (this.frMain); + if ((this.Child != null)) { + this.Child.ShowAll (); + } + this.DefaultWidth = 836; + this.DefaultHeight = 684; + this.Show (); + this.DeleteEvent += new global::Gtk.DeleteEventHandler (this.OnDeleteEvent); + this.btnLogin2.Clicked += new global::System.EventHandler (this.OnBtnLogin2Clicked); + this.btnQueueSong.Clicked += new global::System.EventHandler (this.OnBtnQueueSongClicked); + this.btnSearch.Clicked += new global::System.EventHandler (this.OnBtnSearchClicked); + } +} diff --git a/SubsonicMono/SubsonicMono/gtk-gui/generated.cs b/SubsonicMono/SubsonicMono/gtk-gui/generated.cs new file mode 100644 index 0000000..8463caa --- /dev/null +++ b/SubsonicMono/SubsonicMono/gtk-gui/generated.cs @@ -0,0 +1,29 @@ + +// This file has been generated by the GUI designer. Do not modify. +namespace Stetic +{ + internal class Gui + { + private static bool initialized; + + static internal void Initialize (Gtk.Widget iconRenderer) + { + if ((Stetic.Gui.initialized == false)) { + Stetic.Gui.initialized = true; + } + } + } + + internal class ActionGroups + { + public static Gtk.ActionGroup GetActionGroup (System.Type type) + { + return Stetic.ActionGroups.GetActionGroup (type.FullName); + } + + public static Gtk.ActionGroup GetActionGroup (string name) + { + return null; + } + } +} diff --git a/SubsonicMono/SubsonicMono/gtk-gui/gui.stetic b/SubsonicMono/SubsonicMono/gtk-gui/gui.stetic new file mode 100644 index 0000000..ec3cc20 --- /dev/null +++ b/SubsonicMono/SubsonicMono/gtk-gui/gui.stetic @@ -0,0 +1,215 @@ + + + + .. + 2.12 + + + + + + + + + MainWindow + CenterOnParent + + + + + False + + + + Subsonic + + + 9 + 10 + + + + + + True + True + + + + 9 + 71 + + + + + + Server URL: + + + 13 + 46 + + + + + + Username: + + + 15 + 110 + + + + + + True + True + + + + 11 + 138 + + + + + + Password: + + + 13 + 176 + + + + + + True + True + False + + + + 13 + 199 + + + + + + + + + + + + 262 + 348 + True + In + + + + None + + + + + + + + 10 + 295 + + + + + + True + TextOnly + Login2 + True + + + + 14 + 241 + + + + + + 208 + 338 + True + In + + + + None + + + + + + + + 315 + 296 + + + + + + True + TextOnly + > + True + + + + 280 + 425 + + + + + + True + TextOnly + Search + True + + + + 323 + 247 + + + + + + True + True + + + + 315 + 213 + + + + + + Search + + + 318 + 179 + + + + + + \ No newline at end of file diff --git a/SubsonicMono/SubsonicWeb/Default.aspx b/SubsonicMono/SubsonicWeb/Default.aspx new file mode 100644 index 0000000..48d003e --- /dev/null +++ b/SubsonicMono/SubsonicWeb/Default.aspx @@ -0,0 +1,25 @@ +<%@ Page Language="C#" Inherits="SubsonicWeb.Default" %> + + + + Default + + +
+ +
+ Username +
+ +
+
+ Password +
+ +
+ +
+
+
+ + diff --git a/SubsonicMono/SubsonicWeb/Default.aspx.cs b/SubsonicMono/SubsonicWeb/Default.aspx.cs new file mode 100644 index 0000000..659c5c1 --- /dev/null +++ b/SubsonicMono/SubsonicWeb/Default.aspx.cs @@ -0,0 +1,29 @@ + +using System; +using System.Web; +using System.Web.UI; +using SubsonicAPI; + +namespace SubsonicWeb +{ + + + public partial class Default : System.Web.UI.Page + { + protected void btnLogIn_Click (object sender, System.EventArgs e) + { + string server = "thefij.kicks-ass.net:4040"; + string username = tbUsername.Text; + string password = tbPassword.Text; + + Subsonic.appName = "IansWebApp"; + string results = Subsonic.LogIn(server, username, password); + + if (results != "") + Response.Redirect("Library.aspx"); + } + + + } +} + diff --git a/SubsonicMono/SubsonicWeb/Default.aspx.designer.cs b/SubsonicMono/SubsonicWeb/Default.aspx.designer.cs new file mode 100644 index 0000000..4bfe2ff --- /dev/null +++ b/SubsonicMono/SubsonicWeb/Default.aspx.designer.cs @@ -0,0 +1,30 @@ +// ------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Mono Runtime Version: 2.0.50727.1433 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +// ------------------------------------------------------------------------------ + +namespace SubsonicWeb { + + + public partial class Default { + + protected System.Web.UI.HtmlControls.HtmlForm form1; + + protected System.Web.UI.WebControls.Panel pnlLogin; + + protected System.Web.UI.WebControls.Label lblUsername; + + protected System.Web.UI.WebControls.TextBox tbUsername; + + protected System.Web.UI.WebControls.Label lblPassword; + + protected System.Web.UI.WebControls.TextBox tbPassword; + + protected System.Web.UI.WebControls.Button btnLogIn; + } +} diff --git a/SubsonicMono/SubsonicWeb/Global.asax b/SubsonicMono/SubsonicWeb/Global.asax new file mode 100644 index 0000000..8b55bbf --- /dev/null +++ b/SubsonicMono/SubsonicWeb/Global.asax @@ -0,0 +1 @@ +<%@ Application Inherits="SubsonicWeb.Global" %> diff --git a/SubsonicMono/SubsonicWeb/Global.asax.cs b/SubsonicMono/SubsonicWeb/Global.asax.cs new file mode 100644 index 0000000..aff4fc8 --- /dev/null +++ b/SubsonicMono/SubsonicWeb/Global.asax.cs @@ -0,0 +1,49 @@ + +using System; +using System.Collections; +using System.ComponentModel; +using System.Web; +using System.Web.SessionState; + +namespace SubsonicWeb +{ + + + public class Global : System.Web.HttpApplication + { + + protected virtual void Application_Start (Object sender, EventArgs e) + { + } + + protected virtual void Session_Start (Object sender, EventArgs e) + { + + } + + protected virtual void Application_BeginRequest (Object sender, EventArgs e) + { + } + + protected virtual void Application_EndRequest (Object sender, EventArgs e) + { + } + + protected virtual void Application_AuthenticateRequest (Object sender, EventArgs e) + { + } + + protected virtual void Application_Error (Object sender, EventArgs e) + { + } + + protected virtual void Session_End (Object sender, EventArgs e) + { + } + + protected virtual void Application_End (Object sender, EventArgs e) + { + } + } +} + diff --git a/SubsonicMono/SubsonicWeb/Library.aspx b/SubsonicMono/SubsonicWeb/Library.aspx new file mode 100644 index 0000000..4e0c236 --- /dev/null +++ b/SubsonicMono/SubsonicWeb/Library.aspx @@ -0,0 +1,32 @@ +<%@ Page Language="C#" Inherits="SubsonicWeb.Library" %> + + + + Library + + +
+ + + + + +
+ + + + +
+
+
+
+ Select an Artist + + + + + +
+
+ + diff --git a/SubsonicMono/SubsonicWeb/Library.aspx.cs b/SubsonicMono/SubsonicWeb/Library.aspx.cs new file mode 100644 index 0000000..ab11212 --- /dev/null +++ b/SubsonicMono/SubsonicWeb/Library.aspx.cs @@ -0,0 +1,82 @@ + +using System; +using System.Web; +using System.Web.UI; +using System.Web.UI.WebControls; +using SubsonicAPI; +using System.Collections.Generic; +using System.Data; +using System.Data.Common; +using Mono.Data.Sqlite; + +namespace SubsonicWeb +{ + + + public partial class Library : System.Web.UI.Page + { + protected override void OnLoad (EventArgs e) + { + base.OnLoad (e); + + DataTable dtArtists = new DataTable(); + + #region fetch data for artists + + Mono.Data.Sqlite.SqliteConnection cn = new Mono.Data.Sqlite.SqliteConnection("library.sqlite"); + Mono.Data.Sqlite.SqliteCommand comm = new Mono.Data.Sqlite.SqliteCommand(cn); + Mono.Data.Sqlite.SqliteDataAdapter adapter = new Mono.Data.Sqlite.SqliteDataAdapter(comm); + comm.CommandText = @" +SELECT name, id, fetched +FROM artists +"; + adapter.Fill(dtArtists); + + #endregion + + if (dtArtists.Rows.Count == 0) + { + List artists = Subsonic.GetIndexes(); + + foreach (SubsonicItem artist in artists) + { + DataRow dr = dtArtists.NewRow(); + dr["name"] = artist.name; + dr["id"] = artist.id; + dr["feteched"] = DateTime.Now.ToString(); + dtArtists.Rows.Add(dr); + + comm = new Mono.Data.Sqlite.SqliteCommand(cn); + comm.CommandText = @" +INSERT INTO artists (name, id, fetched) +VALUES(@name, @id, @fetched); +"; + comm.Parameters.AddWithValue("@name", artist.name); + comm.Parameters.AddWithValue("@id", artist.id); + comm.Parameters.AddWithValue("@fetched", DateTime.Now.ToString()); + + if (cn.State != ConnectionState.Open) + cn.Open(); + comm.ExecuteNonQuery(); + } + + if (cn.State != ConnectionState.Closed) + cn.Close(); + } + + rptArtists.DataSource = dtArtists; + rptArtists.DataBind(); + } + + protected void rptArtists_ItemDataBound (object sender, System.Web.UI.WebControls.RepeaterItemEventArgs e) + { + if (e.Item.ItemType == ListItemType.Item || e.Item.ItemType == ListItemType.AlternatingItem) + { + HyperLink hlnkArtist = (HyperLink)e.Item.FindControl("hlnkArtist"); + } + } + + + } +} + diff --git a/SubsonicMono/SubsonicWeb/Library.aspx.designer.cs b/SubsonicMono/SubsonicWeb/Library.aspx.designer.cs new file mode 100644 index 0000000..82f11a3 --- /dev/null +++ b/SubsonicMono/SubsonicWeb/Library.aspx.designer.cs @@ -0,0 +1,28 @@ +// ------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Mono Runtime Version: 2.0.50727.1433 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +// ------------------------------------------------------------------------------ + +namespace SubsonicWeb { + + + public partial class Library { + + protected System.Web.UI.HtmlControls.HtmlForm form1; + + protected System.Web.UI.WebControls.Repeater rptArtists; + + protected System.Web.UI.WebControls.LinkButton lbtnArtist; + + protected System.Web.UI.WebControls.HyperLink hlnkArtist; + + protected System.Web.UI.WebControls.Label lblSelArtist; + + protected System.Web.UI.WebControls.Repeater rptItems; + } +} diff --git a/SubsonicMono/SubsonicWeb/SubsonicWeb.csproj b/SubsonicMono/SubsonicWeb/SubsonicWeb.csproj new file mode 100644 index 0000000..d9b74cf --- /dev/null +++ b/SubsonicMono/SubsonicWeb/SubsonicWeb.csproj @@ -0,0 +1 @@ + Debug AnyCPU 9.0.21022 2.0 {91BB17F9-5F24-4329-BA99-8A96B800416E} {349C5851-65DF-11DA-9384-00065B846F21};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} Library SubsonicWeb SubsonicWeb v3.5 true full false bin DEBUG prompt 4 false none false bin prompt 4 false Global.asax Default.aspx Default.aspx Library.aspx Library.aspx {AD32EC84-3CE8-4E20-934E-6B4F40A9BC08} SubsonicAPI \ No newline at end of file diff --git a/SubsonicMono/SubsonicWeb/library.sqlite b/SubsonicMono/SubsonicWeb/library.sqlite new file mode 100644 index 0000000..d32e02f Binary files /dev/null and b/SubsonicMono/SubsonicWeb/library.sqlite differ diff --git a/SubsonicMono/SubsonicWeb/web.config b/SubsonicMono/SubsonicWeb/web.config new file mode 100644 index 0000000..459a7e2 --- /dev/null +++ b/SubsonicMono/SubsonicWeb/web.config @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/SubsonicPlayer/SubsonicAPI/Properties/AssemblyInfo.cs b/SubsonicPlayer/SubsonicAPI/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..7f56b66 --- /dev/null +++ b/SubsonicPlayer/SubsonicAPI/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("SubsonicAPI")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("IBM")] +[assembly: AssemblyProduct("SubsonicAPI")] +[assembly: AssemblyCopyright("Copyright © IBM 2010")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("cfc7a286-1cf6-4b0d-a011-fb2a3abcbb9a")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/SubsonicPlayer/SubsonicAPI/SubsonicAPI.cs b/SubsonicPlayer/SubsonicAPI/SubsonicAPI.cs new file mode 100644 index 0000000..471f558 --- /dev/null +++ b/SubsonicPlayer/SubsonicAPI/SubsonicAPI.cs @@ -0,0 +1,383 @@ +/************************************************************************** + Subsonic Csharp + Copyright (C) 2010 Ian Fijolek + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +**************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Text; +using System.Net; +using System.IO; +using System.Xml; + +namespace SubsonicAPI +{ + #region Classes + + public class SubsonicItem + { + public string Name; + public string id; + + public enum SubsonicItemType + { + Folder, Song + } + + public SubsonicItemType ItemType; + + public override string ToString() + { + return Name; + } + } + + public class MusicFolder : SubsonicItem + { + #region private vars + + private List _Folders; + private List _Songs; + + #endregion private vars + + #region properties + + public List Folders + { + get { return _Folders; } + set { _Folders = value; } + } + + public List Songs + { + get { return _Songs; } + set { _Songs = value; } + } + + #endregion properties + + public MusicFolder() + { + _Folders = new List(); + _Songs = new List(); + + base.ItemType = SubsonicItemType.Folder; + } + + public MusicFolder(string theName, string theId) + { + _Folders = new List(); + _Songs = new List(); + + base.Name = theName; + base.id = theId; + + base.ItemType = SubsonicItemType.Folder; + } + + ~MusicFolder() { } + + public void AddSong(string title, string id) + { + Song newSong = new Song(title, id); + _Songs.Add(newSong); + } + + public void AddFolder(string name, string id) + { + MusicFolder newFolder = new MusicFolder(name, id); + _Folders.Add(newFolder); + } + + public Song FindSong(string theTitle) + { + Song theSong = _Songs.Find( + delegate(Song sng) + { + return sng.Name == theTitle; + } + ); + + return theSong; + } + + public MusicFolder FindFolder(string theFolderName) + { + MusicFolder theFolder = _Folders.Find( + delegate(MusicFolder fldr) + { + return fldr.Name == theFolderName; + } + ); + + return theFolder; + } + } + + public class Song : SubsonicItem + { + public Song() + { + base.ItemType = SubsonicItemType.Song; + } + + public Song(string theTitle, string theId) + { + Name = theTitle; + id = theId; + + base.ItemType = SubsonicItemType.Song; + } + } + + #endregion Classes + + /// + /// Open Source C# Implementation of the Subsonic API + /// http://www.subsonic.org/pages/api.jsp + /// + public static class Subsonic + { + // Should be set from application layer when the application is loaded + public static string appName; + + // Version of the REST API implemented + private static string apiVersion = "1.3.0"; + + // Set with the login method + static string server; + static string authHeader; + + /// + /// Takes parameters for server, username and password to generate an auth header + /// and Pings the server + /// + /// + /// + /// + /// Resulting XML (Future boolean) + public static string LogIn(string theServer, string user, string password) + { + string result = "Nothing Happened"; + + server = theServer; + authHeader = user + ":" + password; + authHeader = Convert.ToBase64String(Encoding.Default.GetBytes(authHeader)); + + Stream theStream = MakeGenericRequest("ping", null); + + StreamReader sr = new StreamReader(theStream); + + result = sr.ReadToEnd(); + + /// TODO: Parse the result and determine if logged in or not + + return result; + } + + /// + /// Uses the Auth Header for logged in user to make an HTTP request to the server + /// with the given Subsonic API method and parameters + /// + /// + /// + /// Datastream of the server response + public static Stream MakeGenericRequest(string method, Dictionary parameters) + { + // Check to see if Logged In yet + if (string.IsNullOrEmpty(authHeader)) + { + // Throw a Not Logged In exception + Exception e = new Exception("No Authorization header. Must Log In first"); + return null; + } + else + { + if (!method.EndsWith(".view")) + method += ".view"; + + string requestURL = BuildRequestURL(method, parameters); + + WebRequest theRequest = WebRequest.Create(requestURL); + theRequest.Method = "GET"; + + theRequest.Headers["Authorization"] = "Basic " + authHeader; + + WebResponse response = theRequest.GetResponse(); + + Stream dataStream = response.GetResponseStream(); + + return dataStream; + } + } + + /// + /// Creates a URL for a request but does not make the actual request using set login credentials an dmethod and parameters + /// + /// + /// + /// Proper Subsonic API URL for a request + public static string BuildRequestURL(string method, Dictionary parameters) + { + string requestURL = "http://" + server + "/rest/" + method + "?v=" + apiVersion + "&c=" + appName; + if (parameters != null) + { + foreach (KeyValuePair parameter in parameters) + { + requestURL += "&" + parameter.Key + "=" + parameter.Value; + } + } + return requestURL; + } + + /// + /// Returns an indexed structure of all artists. + /// + /// Required: No; If specified, only return artists in the music folder with the given ID. + /// Required: No; If specified, only return a result if the artist collection has changed since the given time. + /// Dictionary, Key = Artist and Value = id + public static Dictionary GetIndexes(string musicFolderId = "", string ifModifiedSince = "") + { + // Load the parameters if provided + Dictionary parameters = new Dictionary(); + if (!string.IsNullOrEmpty(musicFolderId)) + parameters.Add("musicFolderId", musicFolderId); + + if (!string.IsNullOrEmpty(ifModifiedSince)) + parameters.Add("ifModifiedSince", ifModifiedSince); + + // Make the request + Stream theStream = MakeGenericRequest("getIndexes", parameters); + // Read the response as a string + StreamReader sr = new StreamReader(theStream); + string result = sr.ReadToEnd(); + + // Parse the resulting XML string into an XmlDocument + XmlDocument myXML = new XmlDocument(); + myXML.LoadXml(result); + + // Parse the XML document into a Dictionary + Dictionary artists = new Dictionary(); + if (myXML.ChildNodes[1].Name == "subsonic-response") + { + if (myXML.ChildNodes[1].FirstChild.Name == "indexes") + { + int i = 0; + for (i = 0; i < myXML.ChildNodes[1].FirstChild.ChildNodes.Count; i++) + { + int j = 0; + for (j = 0; j < myXML.ChildNodes[1].FirstChild.ChildNodes[i].ChildNodes.Count; j++) + { + string artist = myXML.ChildNodes[1].FirstChild.ChildNodes[i].ChildNodes[j].Attributes["name"].Value; + string id = myXML.ChildNodes[1].FirstChild.ChildNodes[i].ChildNodes[j].Attributes["id"].Value; + + artists.Add(artist, id); + } + } + } + } + + return artists; + } + + /// + /// Streams a given music file. (Renamed from request name "stream") + /// + /// Required: Yes; A string which uniquely identifies the file to stream. + /// Obtained by calls to getMusicDirectory. + /// Required: No; If specified, the server will attempt to + /// limit the bitrate to this value, in kilobits per second. If set to zero, no limit + /// is imposed. Legal values are: 0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256 and 320. + /// + public static Stream StreamSong(string id, int? maxBitRate = null) + { + // Reades the id of the song and sets it as a parameter + Dictionary theParameters = new Dictionary(); + theParameters.Add("id", id); + if (maxBitRate.HasValue) + theParameters.Add("maxBitRate", maxBitRate.ToString()); + + // Makes the request + Stream theStream = MakeGenericRequest("stream", theParameters); + + return theStream; + } + + + /// + /// Returns a listing of all files in a music directory. Typically used to get list of albums for an artist, or list of songs for an album. + /// + /// A string which uniquely identifies the music folder. Obtained by calls to getIndexes or getMusicDirectory. + /// MusicFolder object containing info for the specified directory + public static MusicFolder GetMusicDirectory(string id) + { + Dictionary theParameters = new Dictionary(); + theParameters.Add("id", id); + Stream theStream = MakeGenericRequest("getMusicDirectory", theParameters); + + StreamReader sr = new StreamReader(theStream); + + string result = sr.ReadToEnd(); + + XmlDocument myXML = new XmlDocument(); + myXML.LoadXml(result); + + MusicFolder theFolder = new MusicFolder("ArtistFolder", id); + + if (myXML.ChildNodes[1].Name == "subsonic-response") + { + if (myXML.ChildNodes[1].FirstChild.Name == "directory") + { + theFolder.Name = myXML.ChildNodes[1].FirstChild.Attributes["name"].Value; + theFolder.id = myXML.ChildNodes[1].FirstChild.Attributes["id"].Value; + + int i = 0; + for (i = 0; i < myXML.ChildNodes[1].FirstChild.ChildNodes.Count; i++) + { + bool isDir = bool.Parse(myXML.ChildNodes[1].FirstChild.ChildNodes[i].Attributes["isDir"].Value); + string title = myXML.ChildNodes[1].FirstChild.ChildNodes[i].Attributes["title"].Value; + string theId = myXML.ChildNodes[1].FirstChild.ChildNodes[i].Attributes["id"].Value; + + if (isDir) + theFolder.AddFolder(title, theId); + else + theFolder.AddSong(title, theId); + } + } + } + + return theFolder; + } + + /// + /// Returns what is currently being played by all users. Takes no extra parameters. + /// + public static List GetNowPlaying() + { + List nowPlaying = new List(); + + Dictionary theParameters = new Dictionary(); + Stream theStream = MakeGenericRequest("getNowPlaying", theParameters); + StreamReader sr = new StreamReader(theStream); + string result = sr.ReadToEnd(); + + + return nowPlaying; + } + } + +} \ No newline at end of file diff --git a/SubsonicPlayer/SubsonicAPI/SubsonicAPI.csproj b/SubsonicPlayer/SubsonicAPI/SubsonicAPI.csproj new file mode 100644 index 0000000..b2d5cef --- /dev/null +++ b/SubsonicPlayer/SubsonicAPI/SubsonicAPI.csproj @@ -0,0 +1,54 @@ + + + + Debug + AnyCPU + 8.0.30703 + 2.0 + {8EFE676D-0D2D-466A-B6D3-91F93FE71ADC} + Library + Properties + SubsonicAPI + SubsonicAPI + v4.0 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/SubsonicPlayer/SubsonicAPI/SubsonicAPI.pidb b/SubsonicPlayer/SubsonicAPI/SubsonicAPI.pidb new file mode 100644 index 0000000..a1cb41d Binary files /dev/null and b/SubsonicPlayer/SubsonicAPI/SubsonicAPI.pidb differ diff --git a/SubsonicPlayer/SubsonicPlayer.sln b/SubsonicPlayer/SubsonicPlayer.sln new file mode 100644 index 0000000..5f02cf1 --- /dev/null +++ b/SubsonicPlayer/SubsonicPlayer.sln @@ -0,0 +1,42 @@ + +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual C# Express 2010 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SubsonicPlayer", "SubsonicPlayer\SubsonicPlayer.csproj", "{DCD7B803-6052-4B77-8DF5-91B0BBA2CA19}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SubsonicAPI", "SubsonicAPI\SubsonicAPI.csproj", "{8EFE676D-0D2D-466A-B6D3-91F93FE71ADC}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|Mixed Platforms = Debug|Mixed Platforms + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|Mixed Platforms = Release|Mixed Platforms + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {DCD7B803-6052-4B77-8DF5-91B0BBA2CA19}.Debug|Any CPU.ActiveCfg = Debug|x86 + {DCD7B803-6052-4B77-8DF5-91B0BBA2CA19}.Debug|Mixed Platforms.ActiveCfg = Debug|x86 + {DCD7B803-6052-4B77-8DF5-91B0BBA2CA19}.Debug|Mixed Platforms.Build.0 = Debug|x86 + {DCD7B803-6052-4B77-8DF5-91B0BBA2CA19}.Debug|x86.ActiveCfg = Debug|x86 + {DCD7B803-6052-4B77-8DF5-91B0BBA2CA19}.Debug|x86.Build.0 = Debug|x86 + {DCD7B803-6052-4B77-8DF5-91B0BBA2CA19}.Release|Any CPU.ActiveCfg = Release|x86 + {DCD7B803-6052-4B77-8DF5-91B0BBA2CA19}.Release|Mixed Platforms.ActiveCfg = Release|x86 + {DCD7B803-6052-4B77-8DF5-91B0BBA2CA19}.Release|Mixed Platforms.Build.0 = Release|x86 + {DCD7B803-6052-4B77-8DF5-91B0BBA2CA19}.Release|x86.ActiveCfg = Release|x86 + {DCD7B803-6052-4B77-8DF5-91B0BBA2CA19}.Release|x86.Build.0 = Release|x86 + {8EFE676D-0D2D-466A-B6D3-91F93FE71ADC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8EFE676D-0D2D-466A-B6D3-91F93FE71ADC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8EFE676D-0D2D-466A-B6D3-91F93FE71ADC}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {8EFE676D-0D2D-466A-B6D3-91F93FE71ADC}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {8EFE676D-0D2D-466A-B6D3-91F93FE71ADC}.Debug|x86.ActiveCfg = Debug|Any CPU + {8EFE676D-0D2D-466A-B6D3-91F93FE71ADC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8EFE676D-0D2D-466A-B6D3-91F93FE71ADC}.Release|Any CPU.Build.0 = Release|Any CPU + {8EFE676D-0D2D-466A-B6D3-91F93FE71ADC}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {8EFE676D-0D2D-466A-B6D3-91F93FE71ADC}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {8EFE676D-0D2D-466A-B6D3-91F93FE71ADC}.Release|x86.ActiveCfg = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/SubsonicPlayer/SubsonicPlayer.suo b/SubsonicPlayer/SubsonicPlayer.suo new file mode 100644 index 0000000..abca5a5 Binary files /dev/null and b/SubsonicPlayer/SubsonicPlayer.suo differ diff --git a/SubsonicPlayer/SubsonicPlayer.userprefs b/SubsonicPlayer/SubsonicPlayer.userprefs new file mode 100644 index 0000000..5d0f522 --- /dev/null +++ b/SubsonicPlayer/SubsonicPlayer.userprefs @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/SubsonicPlayer/SubsonicPlayer/PlayerForm.Designer.cs b/SubsonicPlayer/SubsonicPlayer/PlayerForm.Designer.cs new file mode 100644 index 0000000..f9ecb09 --- /dev/null +++ b/SubsonicPlayer/SubsonicPlayer/PlayerForm.Designer.cs @@ -0,0 +1,285 @@ +namespace WindowsFormsApplication1 +{ + partial class PlayerForm + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.tbServer = new System.Windows.Forms.TextBox(); + this.label1 = new System.Windows.Forms.Label(); + this.label2 = new System.Windows.Forms.Label(); + this.label3 = new System.Windows.Forms.Label(); + this.label4 = new System.Windows.Forms.Label(); + this.tbPassword = new System.Windows.Forms.TextBox(); + this.tbUser = new System.Windows.Forms.TextBox(); + this.btnLogIn = new System.Windows.Forms.Button(); + this.tbResults = new System.Windows.Forms.RichTextBox(); + this.btnGetSongs = new System.Windows.Forms.Button(); + this.tvArtists = new System.Windows.Forms.TreeView(); + this.label5 = new System.Windows.Forms.Label(); + this.lbAlbums = new System.Windows.Forms.ListBox(); + this.label6 = new System.Windows.Forms.Label(); + this.button1 = new System.Windows.Forms.Button(); + this.lbPlaylist = new System.Windows.Forms.ListBox(); + this.label7 = new System.Windows.Forms.Label(); + this.pbSongProgress = new System.Windows.Forms.ProgressBar(); + this.button2 = new System.Windows.Forms.Button(); + this.button3 = new System.Windows.Forms.Button(); + this.SuspendLayout(); + // + // tbServer + // + this.tbServer.Location = new System.Drawing.Point(82, 35); + this.tbServer.Name = "tbServer"; + this.tbServer.Size = new System.Drawing.Size(100, 20); + this.tbServer.TabIndex = 0; + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(13, 13); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(82, 13); + this.label1.TabIndex = 1; + this.label1.Text = "SubSonic Login"; + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point(20, 42); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(41, 13); + this.label2.TabIndex = 2; + this.label2.Text = "Server:"; + // + // label3 + // + this.label3.AutoSize = true; + this.label3.Location = new System.Drawing.Point(20, 78); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(32, 13); + this.label3.TabIndex = 3; + this.label3.Text = "User:"; + // + // label4 + // + this.label4.AutoSize = true; + this.label4.Location = new System.Drawing.Point(20, 114); + this.label4.Name = "label4"; + this.label4.Size = new System.Drawing.Size(56, 13); + this.label4.TabIndex = 4; + this.label4.Text = "Password:"; + // + // tbPassword + // + this.tbPassword.Location = new System.Drawing.Point(82, 107); + this.tbPassword.Name = "tbPassword"; + this.tbPassword.PasswordChar = '*'; + this.tbPassword.Size = new System.Drawing.Size(100, 20); + this.tbPassword.TabIndex = 2; + // + // tbUser + // + this.tbUser.Location = new System.Drawing.Point(82, 75); + this.tbUser.Name = "tbUser"; + this.tbUser.Size = new System.Drawing.Size(100, 20); + this.tbUser.TabIndex = 1; + // + // btnLogIn + // + this.btnLogIn.Location = new System.Drawing.Point(23, 150); + this.btnLogIn.Name = "btnLogIn"; + this.btnLogIn.Size = new System.Drawing.Size(75, 23); + this.btnLogIn.TabIndex = 3; + this.btnLogIn.Text = "Log In"; + this.btnLogIn.UseVisualStyleBackColor = true; + this.btnLogIn.Click += new System.EventHandler(this.btnLogIn_Click); + // + // tbResults + // + this.tbResults.Location = new System.Drawing.Point(23, 499); + this.tbResults.Name = "tbResults"; + this.tbResults.Size = new System.Drawing.Size(278, 131); + this.tbResults.TabIndex = 99; + this.tbResults.Text = ""; + // + // btnGetSongs + // + this.btnGetSongs.Enabled = false; + this.btnGetSongs.Location = new System.Drawing.Point(107, 150); + this.btnGetSongs.Name = "btnGetSongs"; + this.btnGetSongs.Size = new System.Drawing.Size(75, 23); + this.btnGetSongs.TabIndex = 4; + this.btnGetSongs.Text = "Get Songs"; + this.btnGetSongs.UseVisualStyleBackColor = true; + this.btnGetSongs.Click += new System.EventHandler(this.btnGetSongs_Click); + // + // tvArtists + // + this.tvArtists.Location = new System.Drawing.Point(16, 200); + this.tvArtists.Name = "tvArtists"; + this.tvArtists.Size = new System.Drawing.Size(281, 248); + this.tvArtists.TabIndex = 101; + this.tvArtists.AfterSelect += new System.Windows.Forms.TreeViewEventHandler(this.tvArtists_AfterSelect); + // + // label5 + // + this.label5.AutoSize = true; + this.label5.Location = new System.Drawing.Point(13, 183); + this.label5.Name = "label5"; + this.label5.Size = new System.Drawing.Size(35, 13); + this.label5.TabIndex = 102; + this.label5.Text = "Artists"; + // + // lbAlbums + // + this.lbAlbums.FormattingEnabled = true; + this.lbAlbums.Location = new System.Drawing.Point(303, 200); + this.lbAlbums.Name = "lbAlbums"; + this.lbAlbums.Size = new System.Drawing.Size(212, 251); + this.lbAlbums.TabIndex = 103; + this.lbAlbums.SelectedIndexChanged += new System.EventHandler(this.lbAlbums_SelectedIndexChanged); + // + // label6 + // + this.label6.AutoSize = true; + this.label6.Location = new System.Drawing.Point(307, 186); + this.label6.Name = "label6"; + this.label6.Size = new System.Drawing.Size(41, 13); + this.label6.TabIndex = 104; + this.label6.Text = "Albums"; + // + // button1 + // + this.button1.Location = new System.Drawing.Point(188, 150); + this.button1.Name = "button1"; + this.button1.Size = new System.Drawing.Size(75, 23); + this.button1.TabIndex = 105; + this.button1.Text = "Pause"; + this.button1.UseVisualStyleBackColor = true; + this.button1.Click += new System.EventHandler(this.button1_Click); + // + // lbPlaylist + // + this.lbPlaylist.FormattingEnabled = true; + this.lbPlaylist.Location = new System.Drawing.Point(522, 200); + this.lbPlaylist.Name = "lbPlaylist"; + this.lbPlaylist.Size = new System.Drawing.Size(154, 251); + this.lbPlaylist.TabIndex = 106; + // + // label7 + // + this.label7.AutoSize = true; + this.label7.Location = new System.Drawing.Point(524, 186); + this.label7.Name = "label7"; + this.label7.Size = new System.Drawing.Size(39, 13); + this.label7.TabIndex = 107; + this.label7.Text = "Playlist"; + // + // pbSongProgress + // + this.pbSongProgress.Location = new System.Drawing.Point(373, 149); + this.pbSongProgress.Name = "pbSongProgress"; + this.pbSongProgress.Size = new System.Drawing.Size(289, 23); + this.pbSongProgress.TabIndex = 108; + // + // button2 + // + this.button2.Location = new System.Drawing.Point(270, 149); + this.button2.Name = "button2"; + this.button2.Size = new System.Drawing.Size(75, 23); + this.button2.TabIndex = 109; + this.button2.Text = "Skip"; + this.button2.UseVisualStyleBackColor = true; + this.button2.Click += new System.EventHandler(this.button2_Click); + // + // button3 + // + this.button3.Location = new System.Drawing.Point(270, 120); + this.button3.Name = "button3"; + this.button3.Size = new System.Drawing.Size(75, 23); + this.button3.TabIndex = 110; + this.button3.Text = "Stop"; + this.button3.UseVisualStyleBackColor = true; + this.button3.Click += new System.EventHandler(this.button3_Click); + // + // PlayerForm + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(688, 633); + this.Controls.Add(this.button3); + this.Controls.Add(this.button2); + this.Controls.Add(this.pbSongProgress); + this.Controls.Add(this.label7); + this.Controls.Add(this.lbPlaylist); + this.Controls.Add(this.button1); + this.Controls.Add(this.label6); + this.Controls.Add(this.lbAlbums); + this.Controls.Add(this.label5); + this.Controls.Add(this.tvArtists); + this.Controls.Add(this.btnGetSongs); + this.Controls.Add(this.tbResults); + this.Controls.Add(this.btnLogIn); + this.Controls.Add(this.tbUser); + this.Controls.Add(this.tbPassword); + this.Controls.Add(this.label4); + this.Controls.Add(this.label3); + this.Controls.Add(this.label2); + this.Controls.Add(this.label1); + this.Controls.Add(this.tbServer); + this.Name = "PlayerForm"; + this.Text = "Subsonic"; + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.TextBox tbServer; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.Label label3; + private System.Windows.Forms.Label label4; + private System.Windows.Forms.TextBox tbPassword; + private System.Windows.Forms.TextBox tbUser; + private System.Windows.Forms.Button btnLogIn; + private System.Windows.Forms.RichTextBox tbResults; + private System.Windows.Forms.Button btnGetSongs; + private System.Windows.Forms.TreeView tvArtists; + private System.Windows.Forms.Label label5; + private System.Windows.Forms.ListBox lbAlbums; + private System.Windows.Forms.Label label6; + private System.Windows.Forms.Button button1; + private System.Windows.Forms.ListBox lbPlaylist; + private System.Windows.Forms.Label label7; + private System.Windows.Forms.ProgressBar pbSongProgress; + private System.Windows.Forms.Button button2; + private System.Windows.Forms.Button button3; + } +} + diff --git a/SubsonicPlayer/SubsonicPlayer/PlayerForm.cs b/SubsonicPlayer/SubsonicPlayer/PlayerForm.cs new file mode 100644 index 0000000..8c7c19f --- /dev/null +++ b/SubsonicPlayer/SubsonicPlayer/PlayerForm.cs @@ -0,0 +1,574 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Windows.Forms; +using System.Media; +using System.IO; +using NAudio.Wave; +using System.Threading; +using SubsonicAPI; + +namespace WindowsFormsApplication1 +{ + public partial class PlayerForm : Form + { + public PlayerForm() + { + InitializeComponent(); + Subsonic.appName = "IansCsharpApp"; + } + + private void btnLogIn_Click(object sender, EventArgs e) + { + string server = tbServer.Text; + string user = tbUser.Text; + string password = tbPassword.Text; + + string result = Subsonic.LogIn(server, user, password); + + tbResults.Text = result; + + btnGetSongs.Enabled = true; + } + + Dictionary artists; + + private void btnGetSongs_Click(object sender, EventArgs e) + { + artists = Subsonic.GetIndexes(); + tvArtists.BeginUpdate(); + + string firstLetter = ""; + int treeIndex = -1; + foreach (KeyValuePair kvp in artists) + { + string thisFirstLetter = GetFirstLetter(kvp.Key); + if (thisFirstLetter != firstLetter) + { + firstLetter = thisFirstLetter; + tvArtists.Nodes.Add(firstLetter); + treeIndex++; + } + + tvArtists.Nodes[treeIndex].Nodes.Add(kvp.Key); + } + + tvArtists.EndUpdate(); + } + + private string GetFirstLetter(string name) + { + name = name.ToUpper(); + string theFirstLetter = ""; + if (name.StartsWith("THE ")) + theFirstLetter = name.Substring(4, 1); + else if (name.StartsWith("LOS ")) + theFirstLetter = name.Substring(4, 1); + else if (name.StartsWith("LAS ")) + theFirstLetter = name.Substring(4, 1); + else if (name.StartsWith("LA ")) + theFirstLetter = name.Substring(3, 1); + else + theFirstLetter = name.Substring(0, 1); // No leading word + + return theFirstLetter.ToUpper(); + } + + private void tvArtists_AfterSelect(object sender, TreeViewEventArgs e) + { + string theArtist = tvArtists.SelectedNode.Text; + + if (artists.ContainsKey(theArtist)) + { + string theID = artists[theArtist]; + + UpdateAlbumListView(theID); + } + } + + Stack _AlbumListHistory; + + public Stack AlbumListHistory + { + get + { + if (_AlbumListHistory == null) + { + _AlbumListHistory = new Stack(); + _AlbumListHistory.Push("Root"); + } + return _AlbumListHistory; + } + set + { + _AlbumListHistory = value; + } + } + + string _LastAlbumId; + + public string LastAlbumId + { + get + { + if (string.IsNullOrEmpty(_LastAlbumId)) + _LastAlbumId = "Root"; + return _LastAlbumId; + } + set + { + _LastAlbumId = value; + } + } + + private void UpdateAlbumListView(string theID) + { + LastAlbumId = theID; + + MusicFolder FolderContents = Subsonic.GetMusicDirectory(theID); + + lbAlbums.BeginUpdate(); + lbAlbums.Items.Clear(); + + if (AlbumListHistory.Peek() != "Root") + lbAlbums.Items.Add(new MusicFolder("..", AlbumListHistory.Peek())); + + foreach (MusicFolder mf in FolderContents.Folders) + { + lbAlbums.Items.Add(mf); + } + + foreach (Song sg in FolderContents.Songs) + { + lbAlbums.Items.Add(sg); + } + + lbAlbums.EndUpdate(); + } + + MusicPlayer _thePlayer; + + private MusicPlayer thePlayer + { + get + { + if (_thePlayer == null) + _thePlayer = new MusicPlayer(this); + return _thePlayer; + } + set + { + _thePlayer = value; + } + } + + private void lbAlbums_SelectedIndexChanged(object sender, EventArgs e) + { + + SubsonicItem theItem = (SubsonicItem)lbAlbums.SelectedItem; + if (theItem.ItemType == SubsonicItem.SubsonicItemType.Folder) + { + if (theItem.Name == "..") + AlbumListHistory.Pop(); + else + AlbumListHistory.Push(LastAlbumId); + UpdateAlbumListView(theItem.id); + } + else if (theItem.ItemType == SubsonicItem.SubsonicItemType.Song) + { + thePlayer.addToPlaylist((Song)theItem); + thePlayer.play(); + } + } + + private void button1_Click(object sender, EventArgs e) + { + thePlayer.pause(); + } + + public void updatePlaylist(Queue playlist) + { + lbPlaylist.BeginUpdate(); + lbPlaylist.Items.Clear(); + + foreach (Song sng in playlist) + { + lbPlaylist.Items.Add(sng); + } + + lbPlaylist.EndUpdate(); + } + + public void updateSongProgress(int progress) + { + if (progress > 100) + progress = 100; + pbSongProgress.Value = progress; + } + + private void button2_Click(object sender, EventArgs e) + { + thePlayer.skipSong(); + } + + private void button3_Click(object sender, EventArgs e) + { + thePlayer.stop(); + } + } + + public class MusicPlayer + { + private Song currentSong; + private Queue playlist; + + private BackgroundWorker playerThread; + + public PlaybackState playState; + + private PlayerForm mainForm; + + public MusicPlayer() + { + + } + + public MusicPlayer(PlayerForm theMainForm) + { + mainForm = theMainForm; + } + + /// + /// Public method called by the main view to start playing the playlist + /// + public void play() + { + // If there is no backgroundworker initialized, do that + if (playerThread == null) + { + playerThread = new BackgroundWorker(); + playerThread.DoWork += new DoWorkEventHandler(playerThread_DoWork); + playerThread.ProgressChanged += new ProgressChangedEventHandler(playerThread_ProgressChanged); + playerThread.RunWorkerCompleted += new RunWorkerCompletedEventHandler(playerThread_RunWorkerCompleted); + playerThread.WorkerReportsProgress = true; + + } + + // Set state to playing + this.playState = PlaybackState.Playing; + + // start playing the entire queue + playQueue(); + } + + private void playQueue() + { + if (playlist.Count > 0 && this.playState == PlaybackState.Playing) + { + currentSong = playlist.Peek(); + + if (waveOut == null || waveOut.PlaybackState != PlaybackState.Playing) + NewPlaySong(); + // If the player is not busy yet then start it + if (!playerThread.IsBusy) + playerThread.RunWorkerAsync(); + } + else + { + this.playState = PlaybackState.Stopped; + } + } + + IWavePlayer waveOut; + WaveStream mainOutputStream; + WaveChannel32 volumeStream; + + private void NewPlaySong() + { + if (waveOut != null) + { + if (waveOut.PlaybackState == PlaybackState.Playing) + { + return; + } + else if (waveOut.PlaybackState == PlaybackState.Paused) + { + waveOut.Play(); + return; + } + } + else + { + CreateWaveOut(); + + mainOutputStream = CreateInputStream(); + //trackBarPosition.Maximum = (int)mainOutputStream.TotalTime.TotalSeconds; + //labelTotalTime.Text = String.Format("{0:00}:{1:00}", (int)mainOutputStream.TotalTime.TotalMinutes, + // mainOutputStream.TotalTime.Seconds); + //trackBarPosition.TickFrequency = trackBarPosition.Maximum / 30; + + waveOut.Init(mainOutputStream); + + volumeStream.Volume = 15; //volumeSlider1.Volume; + waveOut.Play(); + } + } + + private WaveStream CreateInputStream() + { + WaveChannel32 inputStream; + + Stream stream = Subsonic.StreamSong(currentSong.id); + + // Try to move this filling of memory stream into the background... + Stream ms = new MemoryStream(); + byte[] buffer = new byte[32768]; + int read; + while ((read = stream.Read(buffer, 0, buffer.Length)) > 0) + { + playerThread.ReportProgress(50); + + ms.Write(buffer, 0, read); + } + + ms.Position = 0; + + WaveStream mp3Reader = new Mp3FileReader(ms); + WaveStream pcmStream = WaveFormatConversionStream.CreatePcmStream(mp3Reader); + WaveStream blockAlignedStream = new BlockAlignReductionStream(pcmStream); + inputStream = new WaveChannel32(blockAlignedStream); + + // we are not going into a mixer so we don't need to zero pad + //((WaveChannel32)inputStream).PadWithZeroes = false; + volumeStream = inputStream; + + //var meteringStream = new MeteringStream(inputStream, inputStream.WaveFormat.SampleRate / 10); + //meteringStream.StreamVolume += new EventHandler(meteringStream_StreamVolume); + + return volumeStream; + } + + private void CreateWaveOut() + { + CloseWaveOut(); + + if (true) + { + WaveCallbackInfo callbackInfo = WaveCallbackInfo.FunctionCallback(); + WaveOut outputDevice = new WaveOut(callbackInfo); + //outputDevice.NumberOfBuffers = 1; + //outputDevice.DesiredLatency = latency; + waveOut = outputDevice; + } + } + + private void CloseWaveOut() + { + if (waveOut != null) + { + waveOut.Stop(); + } + if (mainOutputStream != null) + { + // this one really closes the file and ACM conversion + volumeStream.Close(); + volumeStream = null; + // this one does the metering stream + mainOutputStream.Close(); + mainOutputStream = null; + } + if (waveOut != null) + { + waveOut.Dispose(); + waveOut = null; + } + } + + /// + /// Defines what should be done when the playerThread starts working + /// + /// + /// + private void playerThread_DoWork(object sender, DoWorkEventArgs e) + { + //playSong(); + TrackPlayer(); + } + + /// + /// Updates the main thread on the progress of the player thread + /// + /// + /// + private void playerThread_ProgressChanged(object sender, ProgressChangedEventArgs e) + { + mainForm.updateSongProgress(e.ProgressPercentage); + } + + /// + /// Defines what should be done when the player thread finishes working + /// + /// + /// + private void playerThread_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) + { + SongFinished(); + } + + private void SongFinished() + { + // Remove the top song from the playlist + currentSong = playlist.Dequeue(); + // Update the mainform playlist + mainForm.updatePlaylist(playlist); + + // Start playing the next song + if (playState == PlaybackState.Playing) + playQueue(); + } + + private void TrackPlayer() + { + while (waveOut != null && waveOut.PlaybackState != PlaybackState.Stopped) + { + int progress = (int)((double)mainOutputStream.CurrentTime.TotalSeconds * 100.0 / (double)mainOutputStream.TotalTime.TotalSeconds); + playerThread.ReportProgress(progress); + Thread.Sleep(100); + } + } + + /// + /// Public method to add a song to the playlist + /// + /// + public void addToPlaylist(Song theSong) + { + if (playlist == null) + playlist = new Queue(); + playlist.Enqueue(theSong); + + // Update the playlist on the main form + mainForm.updatePlaylist(playlist); + } + + bool skipThis; + + /// + /// Method that plays whatever the current song is + /// + private void playSong() + { + skipThis = false; + Stream stream = Subsonic.StreamSong(currentSong.id); + + // Try to move this filling of memory stream into the background... + Stream ms = new MemoryStream(); + byte[] buffer = new byte[32768]; + int read; + while ((read = stream.Read(buffer, 0, buffer.Length)) > 0) + { + playerThread.ReportProgress(50); + + ms.Write(buffer, 0, read); + } + + ms.Position = 0; + Mp3FileReader mp3Reader = new Mp3FileReader(ms); + WaveStream blockAlignedStream = + new BlockAlignReductionStream( + WaveFormatConversionStream.CreatePcmStream(mp3Reader)); + WaveOut waveOut; + waveOut = new WaveOut(WaveCallbackInfo.FunctionCallback()); + waveOut.Init(blockAlignedStream); + waveOut.Play(); + playState = PlaybackState.Playing; + bool songEnd = false; + while (playState != PlaybackState.Stopped && !songEnd && !skipThis) + { + if (waveOut.PlaybackState == PlaybackState.Stopped) + songEnd = true; + else + { + switch (playState) + { + case PlaybackState.Paused: + waveOut.Pause(); + break; + case PlaybackState.Playing: + if (waveOut.PlaybackState != PlaybackState.Playing) + waveOut.Play(); + else + { + int progress = (int)(100.0 * mp3Reader.CurrentTime.TotalSeconds / mp3Reader.TotalTime.TotalSeconds); + playerThread.ReportProgress(progress); + Thread.Sleep(100); + } + break; + default: + break; + } + } + } + //if (playState == PlaybackState.Stopped) + waveOut.Stop(); + //waveOut.Dispose(); + } + + internal void pause() + { + //if (playState == PlaybackState.Playing) + // playState = PlaybackState.Paused; + //else if (playState == PlaybackState.Paused) + // playState = PlaybackState.Playing; + + if (waveOut != null) + { + if (waveOut.PlaybackState == PlaybackState.Playing) + { + playState = PlaybackState.Paused; + waveOut.Pause(); + } + else if (waveOut.PlaybackState == PlaybackState.Paused) + { + playState = PlaybackState.Playing; + waveOut.Play(); + } + } + } + + internal void skipSong() + { + if (waveOut != null) + { + if (waveOut.PlaybackState == PlaybackState.Playing) + { + waveOut.Stop(); + } + else if (waveOut.PlaybackState == PlaybackState.Paused) + { + waveOut.Stop(); + } + } + } + + internal void stop() + { + playState = PlaybackState.Stopped; + if (waveOut != null) + { + if (waveOut.PlaybackState == PlaybackState.Playing) + { + waveOut.Stop(); + } + else if (waveOut.PlaybackState == PlaybackState.Paused) + { + waveOut.Stop(); + } + } + } + } +} diff --git a/SubsonicPlayer/SubsonicPlayer/PlayerForm.resx b/SubsonicPlayer/SubsonicPlayer/PlayerForm.resx new file mode 100644 index 0000000..29dcb1b --- /dev/null +++ b/SubsonicPlayer/SubsonicPlayer/PlayerForm.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/SubsonicPlayer/SubsonicPlayer/Program.cs b/SubsonicPlayer/SubsonicPlayer/Program.cs new file mode 100644 index 0000000..d8597e7 --- /dev/null +++ b/SubsonicPlayer/SubsonicPlayer/Program.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Windows.Forms; + +namespace WindowsFormsApplication1 +{ + static class Program + { + /// + /// The main entry point for the application. + /// + [STAThread] + static void Main() + { + Application.EnableVisualStyles(); + Application.SetCompatibleTextRenderingDefault(false); + Application.Run(new PlayerForm()); + } + } +} diff --git a/SubsonicPlayer/SubsonicPlayer/Properties/AssemblyInfo.cs b/SubsonicPlayer/SubsonicPlayer/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..94a4e92 --- /dev/null +++ b/SubsonicPlayer/SubsonicPlayer/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("WindowsFormsApplication1")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("IBM")] +[assembly: AssemblyProduct("WindowsFormsApplication1")] +[assembly: AssemblyCopyright("Copyright © IBM 2010")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("52c76658-dc37-4f9e-8b61-3ff686aad7b4")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/SubsonicPlayer/SubsonicPlayer/Properties/Resources.Designer.cs b/SubsonicPlayer/SubsonicPlayer/Properties/Resources.Designer.cs new file mode 100644 index 0000000..5a4fb09 --- /dev/null +++ b/SubsonicPlayer/SubsonicPlayer/Properties/Resources.Designer.cs @@ -0,0 +1,71 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.1 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace WindowsFormsApplication1.Properties +{ + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources + { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() + { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager + { + get + { + if ((resourceMan == null)) + { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("WindowsFormsApplication1.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture + { + get + { + return resourceCulture; + } + set + { + resourceCulture = value; + } + } + } +} diff --git a/SubsonicPlayer/SubsonicPlayer/Properties/Resources.resx b/SubsonicPlayer/SubsonicPlayer/Properties/Resources.resx new file mode 100644 index 0000000..ffecec8 --- /dev/null +++ b/SubsonicPlayer/SubsonicPlayer/Properties/Resources.resx @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/SubsonicPlayer/SubsonicPlayer/Properties/Settings.Designer.cs b/SubsonicPlayer/SubsonicPlayer/Properties/Settings.Designer.cs new file mode 100644 index 0000000..3f000a7 --- /dev/null +++ b/SubsonicPlayer/SubsonicPlayer/Properties/Settings.Designer.cs @@ -0,0 +1,30 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.1 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace WindowsFormsApplication1.Properties +{ + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "10.0.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase + { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default + { + get + { + return defaultInstance; + } + } + } +} diff --git a/SubsonicPlayer/SubsonicPlayer/Properties/Settings.settings b/SubsonicPlayer/SubsonicPlayer/Properties/Settings.settings new file mode 100644 index 0000000..abf36c5 --- /dev/null +++ b/SubsonicPlayer/SubsonicPlayer/Properties/Settings.settings @@ -0,0 +1,7 @@ + + + + + + + diff --git a/SubsonicPlayer/SubsonicPlayer/SubsonicPlayer.csproj b/SubsonicPlayer/SubsonicPlayer/SubsonicPlayer.csproj new file mode 100644 index 0000000..c0cb8ed --- /dev/null +++ b/SubsonicPlayer/SubsonicPlayer/SubsonicPlayer.csproj @@ -0,0 +1,108 @@ + + + + Debug + x86 + 8.0.30703 + 2.0 + {DCD7B803-6052-4B77-8DF5-91B0BBA2CA19} + WinExe + Properties + WindowsFormsApplication1 + SubsonicPlayer + v4.0 + Client + 512 + + + x86 + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + x86 + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + False + ..\..\..\NAudio.dll + + + + + + + + + + + + + + + Form + + + PlayerForm.cs + + + + + PlayerForm.cs + + + ResXFileCodeGenerator + Resources.Designer.cs + Designer + + + True + Resources.resx + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + True + Settings.settings + True + + + + + {6BF52A50-394A-11D3-B153-00C04F79FAA6} + 1 + 0 + 0 + tlbimp + False + True + + + + + {8EFE676D-0D2D-466A-B6D3-91F93FE71ADC} + SubsonicAPI + + + + + \ No newline at end of file diff --git a/SubsonicPlayer/SubsonicPlayer/SubsonicPlayer.csproj.user b/SubsonicPlayer/SubsonicPlayer/SubsonicPlayer.csproj.user new file mode 100644 index 0000000..695b5c7 --- /dev/null +++ b/SubsonicPlayer/SubsonicPlayer/SubsonicPlayer.csproj.user @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/SubsonicPlayer/SubsonicPlayer/SubsonicPlayer.pidb b/SubsonicPlayer/SubsonicPlayer/SubsonicPlayer.pidb new file mode 100644 index 0000000..2335349 Binary files /dev/null and b/SubsonicPlayer/SubsonicPlayer/SubsonicPlayer.pidb differ