From ab3d1b347241407f6a79ea17ccd8c86caaae6700 Mon Sep 17 00:00:00 2001 From: ianfijolek Date: Tue, 1 Feb 2011 14:59:57 -0500 Subject: [PATCH] Initial git commit --- .gitignore | 3 + SubsonicMono/SubsonicAPI/AssemblyInfo.cs | 27 + SubsonicMono/SubsonicAPI/SubsonicAPI.cs | 648 ++++++++++++++++++ SubsonicMono/SubsonicAPI/SubsonicAPI.csproj | 41 ++ SubsonicMono/SubsonicAPI/SubsonicAPI.pidb | Bin 0 -> 11913 bytes SubsonicMono/SubsonicMono.sln | 32 + SubsonicMono/SubsonicMono.userprefs | 25 + SubsonicMono/SubsonicMono/AssemblyInfo.cs | 27 + SubsonicMono/SubsonicMono/ItemLoad.gif | Bin 0 -> 1849 bytes SubsonicMono/SubsonicMono/Main.cs | 17 + SubsonicMono/SubsonicMono/MainWindow.cs | 203 ++++++ SubsonicMono/SubsonicMono/README | 9 + .../SubsonicMono/SubsonicPlayer.csproj | 85 +++ SubsonicMono/SubsonicMono/SubsonicPlayer.pidb | Bin 0 -> 8557 bytes .../SubsonicMono/gtk-gui/MainWindow.cs | 192 ++++++ .../SubsonicMono/gtk-gui/generated.cs | 29 + SubsonicMono/SubsonicMono/gtk-gui/gui.stetic | 215 ++++++ SubsonicMono/SubsonicWeb/Default.aspx | 25 + SubsonicMono/SubsonicWeb/Default.aspx.cs | 29 + .../SubsonicWeb/Default.aspx.designer.cs | 30 + SubsonicMono/SubsonicWeb/Global.asax | 1 + SubsonicMono/SubsonicWeb/Global.asax.cs | 49 ++ SubsonicMono/SubsonicWeb/Library.aspx | 32 + SubsonicMono/SubsonicWeb/Library.aspx.cs | 82 +++ .../SubsonicWeb/Library.aspx.designer.cs | 28 + SubsonicMono/SubsonicWeb/SubsonicWeb.csproj | 1 + SubsonicMono/SubsonicWeb/library.sqlite | Bin 0 -> 5120 bytes SubsonicMono/SubsonicWeb/web.config | 33 + .../SubsonicAPI/Properties/AssemblyInfo.cs | 36 + SubsonicPlayer/SubsonicAPI/SubsonicAPI.cs | 383 +++++++++++ SubsonicPlayer/SubsonicAPI/SubsonicAPI.csproj | 54 ++ SubsonicPlayer/SubsonicAPI/SubsonicAPI.pidb | Bin 0 -> 2244 bytes SubsonicPlayer/SubsonicPlayer.sln | 42 ++ SubsonicPlayer/SubsonicPlayer.suo | Bin 0 -> 26112 bytes SubsonicPlayer/SubsonicPlayer.userprefs | 13 + .../SubsonicPlayer/PlayerForm.Designer.cs | 285 ++++++++ SubsonicPlayer/SubsonicPlayer/PlayerForm.cs | 574 ++++++++++++++++ SubsonicPlayer/SubsonicPlayer/PlayerForm.resx | 120 ++++ SubsonicPlayer/SubsonicPlayer/Program.cs | 21 + .../SubsonicPlayer/Properties/AssemblyInfo.cs | 36 + .../Properties/Resources.Designer.cs | 71 ++ .../SubsonicPlayer/Properties/Resources.resx | 117 ++++ .../Properties/Settings.Designer.cs | 30 + .../Properties/Settings.settings | 7 + .../SubsonicPlayer/SubsonicPlayer.csproj | 108 +++ .../SubsonicPlayer/SubsonicPlayer.csproj.user | 3 + .../SubsonicPlayer/SubsonicPlayer.pidb | Bin 0 -> 17549 bytes 47 files changed, 3763 insertions(+) create mode 100644 .gitignore create mode 100644 SubsonicMono/SubsonicAPI/AssemblyInfo.cs create mode 100644 SubsonicMono/SubsonicAPI/SubsonicAPI.cs create mode 100644 SubsonicMono/SubsonicAPI/SubsonicAPI.csproj create mode 100644 SubsonicMono/SubsonicAPI/SubsonicAPI.pidb create mode 100644 SubsonicMono/SubsonicMono.sln create mode 100644 SubsonicMono/SubsonicMono.userprefs create mode 100644 SubsonicMono/SubsonicMono/AssemblyInfo.cs create mode 100644 SubsonicMono/SubsonicMono/ItemLoad.gif create mode 100644 SubsonicMono/SubsonicMono/Main.cs create mode 100644 SubsonicMono/SubsonicMono/MainWindow.cs create mode 100644 SubsonicMono/SubsonicMono/README create mode 100644 SubsonicMono/SubsonicMono/SubsonicPlayer.csproj create mode 100644 SubsonicMono/SubsonicMono/SubsonicPlayer.pidb create mode 100644 SubsonicMono/SubsonicMono/gtk-gui/MainWindow.cs create mode 100644 SubsonicMono/SubsonicMono/gtk-gui/generated.cs create mode 100644 SubsonicMono/SubsonicMono/gtk-gui/gui.stetic create mode 100644 SubsonicMono/SubsonicWeb/Default.aspx create mode 100644 SubsonicMono/SubsonicWeb/Default.aspx.cs create mode 100644 SubsonicMono/SubsonicWeb/Default.aspx.designer.cs create mode 100644 SubsonicMono/SubsonicWeb/Global.asax create mode 100644 SubsonicMono/SubsonicWeb/Global.asax.cs create mode 100644 SubsonicMono/SubsonicWeb/Library.aspx create mode 100644 SubsonicMono/SubsonicWeb/Library.aspx.cs create mode 100644 SubsonicMono/SubsonicWeb/Library.aspx.designer.cs create mode 100644 SubsonicMono/SubsonicWeb/SubsonicWeb.csproj create mode 100644 SubsonicMono/SubsonicWeb/library.sqlite create mode 100644 SubsonicMono/SubsonicWeb/web.config create mode 100644 SubsonicPlayer/SubsonicAPI/Properties/AssemblyInfo.cs create mode 100644 SubsonicPlayer/SubsonicAPI/SubsonicAPI.cs create mode 100644 SubsonicPlayer/SubsonicAPI/SubsonicAPI.csproj create mode 100644 SubsonicPlayer/SubsonicAPI/SubsonicAPI.pidb create mode 100644 SubsonicPlayer/SubsonicPlayer.sln create mode 100644 SubsonicPlayer/SubsonicPlayer.suo create mode 100644 SubsonicPlayer/SubsonicPlayer.userprefs create mode 100644 SubsonicPlayer/SubsonicPlayer/PlayerForm.Designer.cs create mode 100644 SubsonicPlayer/SubsonicPlayer/PlayerForm.cs create mode 100644 SubsonicPlayer/SubsonicPlayer/PlayerForm.resx create mode 100644 SubsonicPlayer/SubsonicPlayer/Program.cs create mode 100644 SubsonicPlayer/SubsonicPlayer/Properties/AssemblyInfo.cs create mode 100644 SubsonicPlayer/SubsonicPlayer/Properties/Resources.Designer.cs create mode 100644 SubsonicPlayer/SubsonicPlayer/Properties/Resources.resx create mode 100644 SubsonicPlayer/SubsonicPlayer/Properties/Settings.Designer.cs create mode 100644 SubsonicPlayer/SubsonicPlayer/Properties/Settings.settings create mode 100644 SubsonicPlayer/SubsonicPlayer/SubsonicPlayer.csproj create mode 100644 SubsonicPlayer/SubsonicPlayer/SubsonicPlayer.csproj.user create mode 100644 SubsonicPlayer/SubsonicPlayer/SubsonicPlayer.pidb 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 0000000000000000000000000000000000000000..1e55b11c9daacdc8f3234744aab8fefb6b0aa1fe GIT binary patch literal 11913 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 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..5b33f7e54f4e55b6b8774d86d96895db9af044b4 GIT binary patch literal 1849 zcma*odr(tX9tZI2z31lM+(&YVk%mZ}5P~KlG2s=WSbGzm0!x7^P##Mnh7t-jP!X0Q zk_SQ}Po-L1tlDK;6l?(>v)e5ZBQx4|Y-Q?nr@Px3?9h(3ZWr3^tj=`TP57gKr87N$ zp2wWee1GRRCwo_xahnw)5cxNPJbCg2L6DV|6`#+yw6v6!mDS$f9-JvFD^n;GQ&UrZ zzh5jCkByB101O60U0q#p_1BM>Cv-vP?&s4@g_((4_1L=L$(a91)0=J91Gas#R{McE znYG^9*0A5YZ>#;~+Wkn(W5B0^yELIYLP!K}mB~<)AM@1&nqekynuaEGqPrzoH|KodRXJy)%+w_fu3nE5>@Bd_b zqC$EQ;{c`T&?EsNO|igL9gC7Ygxv?aQUEXMq?~>wg{EyW;VcJ37CUF#HjrT=KQO_* zS>M9yydXk18D(+QDJ1>r);Lav_uYKp$T?4vr{Q$lTo&pKv^?(>L-)G2*lwH!Ah7k? z7oH<8h-(KTKt5V6$8gF)C7Io&P5=SjTh)=zV=E2EUhQZP##L8S{d%UK>>+y82>+FV+#^BzW7u3F)Bb>=lYQ%%j`F>ASe zo*cw@V#u6T`A2He;70mR(V&iV&-7{qP~=SRf&jm9-T{*ZeZ}$rd0#6c&fLG^xJcf5 z+p<`wJYgW+_s*V{uI$nMB;%8`S_3>PfGOj3Rq}@Cx^+j?rk92fANSFDBYnOqQ>Vdj z)(|$AhP4t&Lb=Gvo2#3Gl%9<=Gv`Mz?Po@P4iLF!x}GUWJICDlFk-hS^Whyh7x~VH z@0vD1>HYD4&e+~yzS*-sFR{9`{QEEZO1zg7>R&7cHts-6j!xHVdA8eI+ZlVzd%`es zJT@$#GX(gvCJ1oJN%yLBK}{V=V;seo;!w|Yte!W1%5qLNFWqvZW>h&IiH+oPT=b@E zPhGzv5=(Un*X>v`>%8h_nj^NdYcE6NHS_ifkCV$*D)Tqrbu`s;<=t<4 zAHNqNV?6(g<1PY-w@#I-WYFViz?9TrkMr)u0g`O`u|>T;k|2sV*YF^punvT;$SuTy{j3Gv)yqD!R_CF>yR)MzmmYS5v+~R zXAdD%ng9?df;wd8GxR#%3O+gz};Vo;)sK%Bj-q>Oq%R7JU-KD?vYu>#2UjaDo z&8$>5xW~?KPD_#XFToU1hIb*VOMidUr6iYiO0N|i-7s`T8!cFT`rN!^1Pt78J93i6 z5HI1wIM$94m{3SLDvISDe6$ZG1;eq_D9RTaaC>=cO{@Bs>$IlPCPJJ$h$)-3vzNUQ6OsN#_zWxey!_9%hxwH2_dEJi=yY|1c7nDm2_Lm!Cof8-R_+9UkS zcBE(o47yE)oMR(Q=dp1a2wTX5KvvGyLqlWTa7V&!A*|w|)ax~1_~aJ0=_Lilg*0iQk7#ZD EAHN$8j{pDw literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..52bd816bcdd2b4b3cd0810c0f8ecf201842b4ecb GIT binary patch literal 8557 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 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..d32e02f2240afe0480d4f43e6dba7c30188a3931 GIT binary patch literal 5120 zcmeH`K~KUk6vx|bKmroIEy(3%35g4M^5o5F8bcfd=1Am329`i%D0F;)@p=4IeiFNl zMT(dh<85uy_Pw_6_21u1AN%$^q%eJ52Yv`wNJAK-8vuk*h4%vQS&MxobNGz+?4QgE zy8Jk=s$-<#cch-FOJ&9l+5SWzx-OPILivexNBlpCGp4;wN?hqcx5UhdMZ96=&LmUsBF7E@|Zlys>KcEJ90 zL6E-~Z-UYOec6Gs88r$ey;j59Mxs4A%F?kQs8839iO-V#O5!o;QyNZYl + + + + + + + + + + + + + + + + + + + + + + + + + + \ 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 0000000000000000000000000000000000000000..a1cb41d81f1ce03515fcb26f0ac750463e9cbdd0 GIT binary patch literal 2244 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 M9SzBzrXTeG0WTNn&j0`b literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..233534961910aed9d7adba14b9d3dd11fe232e9c GIT binary patch literal 17549 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 literal 0 HcmV?d00001