diff options
author | Hans Oesterholt <hans@oesterholt.net> | 2013-01-09 00:17:04 +0100 |
---|---|---|
committer | Hans Oesterholt <hans@oesterholt.net> | 2013-01-09 00:17:04 +0100 |
commit | 7f19b3743c8785bf3434e720c1343dfc394b55b4 (patch) | |
tree | 748da1e517410430e196b190b0fed3136f919fd4 | |
parent | 4c62e969139f119c95c5a5ee3be7af27d382c548 (diff) |
CueSheets extension
-rw-r--r-- | Extensions.sln | 10 | ||||
-rw-r--r-- | configure.ac | 2 | ||||
-rw-r--r-- | po/banshee-community-extensions.pot | 34 | ||||
-rw-r--r-- | src/CueSheets/Banshee.CueSheets/CueSheet.cs | 222 | ||||
-rw-r--r-- | src/CueSheets/Banshee.CueSheets/CueSheetEntry.cs | 101 | ||||
-rw-r--r-- | src/CueSheets/Banshee.CueSheets/CueSheetsSource.cs | 582 | ||||
-rw-r--r-- | src/CueSheets/CueSheets.addin.xml | 84 | ||||
-rw-r--r-- | src/CueSheets/CueSheets.csproj | 109 | ||||
-rw-r--r-- | src/CueSheets/Makefile.am | 11 | ||||
-rw-r--r-- | src/CueSheets/ThemeIcons/128x128/categories/cueplay.png | bin | 0 -> 15850 bytes | |||
-rw-r--r-- | src/CueSheets/ThemeIcons/22x22/categories/cueplay.png | bin | 0 -> 749 bytes | |||
-rw-r--r-- | src/CueSheets/ThemeIcons/32x32/categories/cueplay.png | bin | 0 -> 1844 bytes | |||
-rw-r--r-- | src/CueSheets/ThemeIcons/48x48/categories/cueplay.png | bin | 0 -> 2036 bytes | |||
-rw-r--r-- | src/CueSheets/ThemeIcons/64x64/categories/cueplay.png | bin | 0 -> 3070 bytes | |||
-rw-r--r-- | src/Makefile.am | 1 |
15 files changed, 1139 insertions, 17 deletions
diff --git a/Extensions.sln b/Extensions.sln index 0621213..319fd61 100644 --- a/Extensions.sln +++ b/Extensions.sln @@ -1,6 +1,8 @@
Microsoft Visual Studio Solution File, Format Version 10.00
-# Visual Studio 2008
+# Visual Studio 2008 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CueSheets", "src\CueSheets\CueSheets.csproj", "{}" +EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DuplicateSongDetector", "src\DuplicateSongDetector\DuplicateSongDetector.csproj", "{1725FB8A-DF47-4CAF-A322-59EE97AAB5DE}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AlbumArtWriter", "src\AlbumArtWriter\AlbumArtWriter.csproj", "{5D56E36A-AB9F-4BAA-BC42-D7503AC0A3A5}"
@@ -87,7 +89,11 @@ Global Release|x86 = Release|x86
Windows|Any CPU = Windows|Any CPU
EndGlobalSection
- GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution + {}.Debug|x86.ActiveCfg = Debug|Any CPU + {}.Debug|x86.Build.0 = Debug|Any CPU + {}.Release|x86.ActiveCfg = Release|Any CPU + {}.Release|x86.Build.0 = Release|Any CPU
{04568A95-2430-41EB-82D6-939F5E7E8403}.Debug|x86.ActiveCfg = Debug|Any CPU
{04568A95-2430-41EB-82D6-939F5E7E8403}.Debug|x86.Build.0 = Debug|Any CPU
{04568A95-2430-41EB-82D6-939F5E7E8403}.Release|x86.ActiveCfg = Release|Any CPU
diff --git a/configure.ac b/configure.ac index d91a378..7c882ce 100644 --- a/configure.ac +++ b/configure.ac @@ -134,6 +134,7 @@ help/Makefile po/Makefile.in src/Makefile +src/CueSheets/Makefile src/AssemblyInfo.cs src/AlarmClock/Makefile @@ -203,6 +204,7 @@ ${PACKAGE}-${VERSION} User Help: ${enable_user_help} (requires gnome-doc-utils >= 0.17.3) Extensions: + CueSheets: yes Alarm Clock: ${enable_AlarmClock} AlbumArtWriter: ${enable_AlbumArtWriter} Ampache: ${enable_Ampache} diff --git a/po/banshee-community-extensions.pot b/po/banshee-community-extensions.pot index f1d3fd8..c3ff106 100644 --- a/po/banshee-community-extensions.pot +++ b/po/banshee-community-extensions.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2012-04-01 20:05+0200\n" +"POT-Creation-Date: 2013-01-09 00:15+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Language-Team: LANGUAGE <LL@li.org>\n" @@ -414,23 +414,23 @@ msgstr "" msgid "Are you sure you want to remove the selected items from your Library?" msgstr "" -#: ../src/FolderSync/Banshee.FolderSync/FolderSyncController.cs:43 +#: ../src/FolderSync/Banshee.FolderSync/FolderSyncController.cs:44 msgid "Sync to Folder" msgstr "" -#: ../src/FolderSync/Banshee.FolderSync/FolderSyncController.cs:46 +#: ../src/FolderSync/Banshee.FolderSync/FolderSyncController.cs:47 msgid "Synchronize to Folder" msgstr "" -#: ../src/FolderSync/Banshee.FolderSync/FolderSyncController.cs:125 +#: ../src/FolderSync/Banshee.FolderSync/FolderSyncController.cs:126 msgid "Preparing sync" msgstr "" -#: ../src/FolderSync/Banshee.FolderSync/FolderSyncController.cs:197 +#: ../src/FolderSync/Banshee.FolderSync/FolderSyncController.cs:220 msgid "Copying" msgstr "" -#: ../src/FolderSync/Banshee.FolderSync/FolderSyncController.cs:215 +#: ../src/FolderSync/Banshee.FolderSync/FolderSyncController.cs:238 msgid "Done!" msgstr "" @@ -442,36 +442,40 @@ msgstr "" msgid "Stop" msgstr "" -#: ../src/FolderSync/Banshee.FolderSync/FolderSyncView.cs:83 +#: ../src/FolderSync/Banshee.FolderSync/FolderSyncView.cs:85 msgid "Start sync" msgstr "" -#: ../src/FolderSync/Banshee.FolderSync/FolderSyncView.cs:90 +#: ../src/FolderSync/Banshee.FolderSync/FolderSyncView.cs:92 msgid "Choose target sync folder" msgstr "" -#: ../src/FolderSync/Banshee.FolderSync/FolderSyncView.cs:93 +#: ../src/FolderSync/Banshee.FolderSync/FolderSyncView.cs:95 msgid "Create M3U Playlist" msgstr "" -#: ../src/FolderSync/Banshee.FolderSync/FolderSyncView.cs:95 +#: ../src/FolderSync/Banshee.FolderSync/FolderSyncView.cs:97 msgid "Overwrite existing files" msgstr "" -#: ../src/FolderSync/Banshee.FolderSync/FolderSyncView.cs:97 +#: ../src/FolderSync/Banshee.FolderSync/FolderSyncView.cs:99 +msgid "Create as symbolic link" +msgstr "" + +#: ../src/FolderSync/Banshee.FolderSync/FolderSyncView.cs:101 msgid "Create subfolders" msgstr "" -#: ../src/FolderSync/Banshee.FolderSync/FolderSyncView.cs:99 -#: ../src/FolderSync/Banshee.FolderSync/FolderSyncView.cs:153 +#: ../src/FolderSync/Banshee.FolderSync/FolderSyncView.cs:103 +#: ../src/FolderSync/Banshee.FolderSync/FolderSyncView.cs:158 msgid "Subfolder depth" msgstr "" -#: ../src/FolderSync/Banshee.FolderSync/FolderSyncView.cs:112 +#: ../src/FolderSync/Banshee.FolderSync/FolderSyncView.cs:116 msgid "Options" msgstr "" -#: ../src/FolderSync/Banshee.FolderSync/FolderSyncView.cs:139 +#: ../src/FolderSync/Banshee.FolderSync/FolderSyncView.cs:143 msgid "Sync to folder:" msgstr "" diff --git a/src/CueSheets/Banshee.CueSheets/CueSheet.cs b/src/CueSheets/Banshee.CueSheets/CueSheet.cs new file mode 100644 index 0000000..990c5a9 --- /dev/null +++ b/src/CueSheets/Banshee.CueSheets/CueSheet.cs @@ -0,0 +1,222 @@ +// +// CueSheet.cs +// +// Authors: +// Cool Extension Author <hans@oesterholt.net> +// +// Copyright (C) 2013 Hans Oesterholt +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +using System; +using System.IO; +using System.Text.RegularExpressions; +using Banshee.CueSheets; +using Banshee.Collection; +using Banshee.Base; + +namespace Banshee.CueSheets +{ + public class CueSheet : MemoryTrackListModel + { + private string _image_file_name; + private string _img_full_path; + private string _music_file_name; + private string _title; + private string _performer; + private CueSheetEntry [] _tracks; + private string _cuefile; + + private void append(CueSheetEntry e) { + if (_tracks==null) { + _tracks=new CueSheetEntry[1]; + _tracks[0]=e; + } else { + CueSheetEntry [] es=new CueSheetEntry[_tracks.Length+1]; + int i=0; + for(i=0;i<_tracks.Length;i++) { + es[i]=_tracks[i]; + } + es[i]=e; + _tracks=es; + } + this.Add (e); + } + + public string imageFileName() { + return _image_file_name; + } + + public string cueFile() { + return _cuefile; + } + + public string musicFileName() { + return _music_file_name; + } + + public string title() { + return _title; + } + + public string performer() { + return _performer; + } + + public CueSheetEntry entry(int i) { + return _tracks[i]; + } + + public int nEntries() { + if (_tracks==null) { return 0; } + else { + return _tracks.Length; + } + } + + public int searchIndex(double offset) { + int k,N; + for(k=0,N=nEntries ();k<N && offset>_tracks[k].offset ();k++); + return k-1; + } + + private string getArtId() { + string aaid=CoverArtSpec.CreateArtistAlbumId (_performer,_title); + if (!CoverArtSpec.CoverExists (aaid)) { + if (File.Exists (_img_full_path)) { + string path=CoverArtSpec.GetPathForNewFile (aaid,_img_full_path); + File.Copy (_img_full_path,path); + Console.WriteLine ("coverartpath="+path); + } + } + string path1=CoverArtSpec.GetPath (aaid); + Console.WriteLine ("coverartpath1="+path1); + return aaid; + } + + public CueSheet (string filename,string directory) { + _cuefile=filename; + Boolean _in_tracks=false; + _image_file_name=""; + _img_full_path=""; + _music_file_name=""; + _title=""; + _performer=""; + _tracks=null; + + string e_perf=""; + string e_title=""; + double e_offset=-1.0; + string aaid=""; + int nr=0; + + using (System.IO.StreamReader sr = System.IO.File.OpenText(filename)) { + string line = ""; + while ((line = sr.ReadLine()) != null) { + line=line.Trim (); + if (line!="") { + //Console.WriteLine ("it="+_in_tracks+", "+line); + if (!_in_tracks) { + if (line.Substring (0,9).ToLower()=="performer") { + string p=line.Substring (9).Trim (); + p=Regex.Replace (p,"[\"]",""); + _performer=p; + } else if (line.Substring (0,5).ToLower ()=="title") { + _title=Regex.Replace (line.Substring (5).Trim (),"[\"]",""); + } else if (line.Substring (0,4).ToLower ()=="file") { + _music_file_name=line.Substring (4).Trim (); + Match m=Regex.Match (_music_file_name,"([\"][^\"]+[\"])"); + Console.WriteLine ("match="+m); + _music_file_name=m.ToString (); + Console.WriteLine ("result="+_music_file_name); + _music_file_name=Regex.Replace (_music_file_name,"[\"]","").Trim (); + _music_file_name=directory+"/"+_music_file_name; + Console.WriteLine ("music file="+_music_file_name); + } else if (line.Substring(0,5).ToLower ()=="track") { + _in_tracks=true; + } else if (line.Substring (0,3).ToLower ()=="rem") { + line=line.Substring (3).Trim (); + if (line.Substring (0,5).ToLower ()=="image") { + _image_file_name=line.Substring (5).Trim (); + _image_file_name=Regex.Replace (_image_file_name,"[\"]","").Trim (); + _img_full_path=directory+"/"+_image_file_name; + } + } + } + + + if (_in_tracks) { + if (aaid=="") { aaid=getArtId (); } + + Console.WriteLine ("line="+line); + if (line.Substring (0,5).ToLower ()=="track") { + if (e_offset>=0) { + nr+=1; + CueSheetEntry e=new CueSheetEntry(_music_file_name,aaid,nr,-1,e_title,e_perf,_title,e_offset); + append (e); + if (nr>1) { + CueSheetEntry ePrev; + ePrev=this.entry (nr-2); + ePrev.setLength(e.offset ()-ePrev.offset()); + } + } + e_perf=_performer; + e_title=""; + e_offset=-1.0; + } else if (line.Substring (0,5).ToLower ()=="title") { + e_title=Regex.Replace (line.Substring (5).Trim (),"[\"]",""); + } else if (line.Substring (0,9).ToLower ()=="performer") { + e_perf=Regex.Replace (line.Substring (9).Trim (),"[\"]",""); + } else if (line.Substring (0,5).ToLower ()=="index") { + string s=line.Substring (5).Trim (); + s=Regex.Replace (s,"^\\s*[0-9]+\\s*",""); + string []parts=Regex.Split(s,"[:]"); + Console.WriteLine ("parts="+parts[0]+","+parts[1]+","+parts[2]); + int min=Convert.ToInt32(parts[0]); + int secs=Convert.ToInt32(parts[1]); + int hsecs=Convert.ToInt32(parts[2]); + e_offset=min*60+secs+(hsecs/100.0); + + } + } + } + } + if (e_offset>=0) { + nr+=1; + CueSheetEntry e=new CueSheetEntry(_music_file_name,aaid,nr,-1,e_title,e_perf,_title,e_offset); + append (e); + if (nr>1) { + CueSheetEntry ePrev; + ePrev=this.entry (nr-2); + ePrev.setLength(e.offset ()-ePrev.offset()); + } + } + + { + int i; + for(i=0;i<nEntries();i++) { + entry (i).setNrOfTracks(nr); + } + } + } + } + } +} + diff --git a/src/CueSheets/Banshee.CueSheets/CueSheetEntry.cs b/src/CueSheets/Banshee.CueSheets/CueSheetEntry.cs new file mode 100644 index 0000000..cf4f308 --- /dev/null +++ b/src/CueSheets/Banshee.CueSheets/CueSheetEntry.cs @@ -0,0 +1,101 @@ +// +// CueSheetEntry.cs +// +// Authors: +// Cool Extension Author <hans@oesterholt.net> +// +// Copyright (C) 2013 Hans Oesterholt +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +using System; + +using Banshee.Collection; +using Banshee.Base; + +namespace Banshee.CueSheets +{ + public class CueSheetEntry : TrackInfo + { + double _offset; + string _performer; + string _title; + string _file; + double _length; + string _art=""; + + public override string ArtworkId { + get { return _art; } + } + + public string file() { + return _file; + } + + public string title() { + return _title; + } + + public string performer() { + return _performer; + } + + public double offset() { + return _offset; + } + + public void setNrOfTracks(int n) { + this.TrackCount=n; + } + + public double length() { + return _length; + } + + public void setLength(double l) { + _length=l; + System.Int64 ticks_100nanosecs=(System.Int64) (l*10000000); // 10 miljoen + this.Duration=new TimeSpan(ticks_100nanosecs); + } + + public CueSheetEntry (string file,String artId,int nr,int cnt,string title,string performer,string album,double offset) { + _file=file; + _title=title; + _performer=performer; + _offset=offset; + _length=-1.0; + + _art=artId; + this.AlbumArtist=performer; + this.TrackTitle=title; + this.AlbumTitle=album; + this.ArtistName=performer; + this.TrackNumber=nr; + this.TrackCount=cnt; + this.DiscNumber=1; + this.CanPlay=true; + this.CanSaveToDatabase=false; + this.Duration=new System.TimeSpan(0,0,10,0); + Console.WriteLine ("file="+_file); + this.Uri=new Hyena.SafeUri(_file,false); + } + } +} + diff --git a/src/CueSheets/Banshee.CueSheets/CueSheetsSource.cs b/src/CueSheets/Banshee.CueSheets/CueSheetsSource.cs new file mode 100644 index 0000000..c4cb87b --- /dev/null +++ b/src/CueSheets/Banshee.CueSheets/CueSheetsSource.cs @@ -0,0 +1,582 @@ +// +// CueSheetsSource.cs +// +// Authors: +// Cool Extension Author <hans@oesterholt.net> +// +// Copyright (C) 2013 Hans Oesterholt +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System; + +using Mono.Addins; + +using Banshee.Base; +using Banshee.Sources; +using Banshee.Sources.Gui; + +// Other namespaces you might want: +using Banshee.ServiceStack; +using Banshee.Preferences; +using Banshee.MediaEngine; +using Banshee.PlaybackController; +using System.IO; + +using Banshee.CueSheets; +using System.Text.RegularExpressions; +using Banshee.Collection; +using Banshee.Library; + +namespace Banshee.CueSheets +{ + // We are inheriting from Source, the top-level, most generic type of Source. + // Other types include (inheritance indicated by indentation): + // DatabaseSource - generic, DB-backed Track source; used by PlaylistSource + // PrimarySource - 'owns' tracks, used by DaapSource, DapSource + // LibrarySource - used by Music, Video, Podcasts, and Audiobooks + public class CueSheetsSource : Source, IBasicPlaybackController, ITrackModelSource + { + // In the sources TreeView, sets the order value for this source, small on top + const int sort_order = 50; + CustomView _view; + TrackListModel _nullModel; + double pbsize=-1.0; + + + public CueSheetsSource () : base (AddinManager.CurrentLocalizer.GetString ("CueSheets"), + AddinManager.CurrentLocalizer.GetString ("CueSheets"), + sort_order, + "hod-cuesheets-2013-01-06") + { + _view=new CustomView(this); + _nullModel=new MemoryTrackListModel(); + Properties.Set<ISourceContents> ("Nereid.SourceContents", _view); + Properties.SetString ("Icon.Name", "cueplay"); + + Hyena.Log.Information ("CueSheets source has been instantiated."); + } + + // A count of 0 will be hidden in the source TreeView + public override int Count { + get { + CueSheet s=_view.getSheet (); + if (s==null) { return 0; } + else { return s.nEntries (); } + } + } + + public CueSheet getSheet() { + return _view.getSheet(); + } + + #region IBasicPlaybackController implementation + public bool First () + { + Console.WriteLine ("First called"); + return true; + } + + public bool Next (bool restart, bool changeImmediately) + { + //Console.WriteLine ("next called"); + return _view.next (); + } + + public bool Previous (bool restart) + { + //Console.WriteLine ("previous:"+restart); + return _view.previous (); + } + #endregion + + #region ITrackModelSource implementation + public void Reload () { + Console.WriteLine ("reloading"); + _view.reLoad(); + } + + public TrackListModel TrackModel { + get { + CueSheet s=_view.getSheet (); + if (s==null) { + return _nullModel; + } else { + return s; + } + } + } + + public bool HasDependencies { get { return false; } } + + public bool CanAddTracks { get { return false; } } + + public bool CanRemoveTracks { get { return false; } } + + public bool CanDeleteTracks { get { return false; } } + + public bool ConfirmRemoveTracks { get { return false; } } + + public bool CanRepeat { get { return true; } } + + public bool CanShuffle { get { return true; } } + + public bool ShowBrowser { get { return false; } } + + public bool Indexable { get { return true; } } + + public void RemoveTracks (Hyena.Collections.Selection selection) { + } + + public void DeleteTracks (Hyena.Collections.Selection selection) { + } + #endregion + + public void OnConfigure (object o, EventArgs ea) { + new CueSheetsConfigDialog (this); + return; + } + + public double getPbSize() { + if (pbsize<0.0) { + pbsize=Banshee.Configuration.ConfigurationClient.Get<double>("cuesheets_zoom",1.0); + } + return pbsize; + } + + public void setPbSize(double s) { + pbsize=s; + Banshee.Configuration.ConfigurationClient.Set<double>("cuesheets_zoom",s); + } + + public string getCueSheetDir() { + string dir=Banshee.Configuration.ConfigurationClient.Get<string>("cuesheets_dir",""); + //string dir=Properties.Get<string>("cuesheets_dir",""); + if (dir=="") { + dir=System.Environment.GetEnvironmentVariable ("HOME"); + } + Console.WriteLine ("getcuesheetdir="+dir); + return dir; + } + + public void setCueSheetDir(string dir) { + //Console.WriteLine ("setcuesheetdir:"+dir); + //Properties.SetString ("cuesheets_dir",dir); + Banshee.Configuration.ConfigurationClient.Set<string>("cuesheets_dir",dir); + } + + private class CueSheetsConfigDialog : Gtk.Dialog { + public CueSheetsConfigDialog(CueSheetsSource src) { + + string dir=src.getCueSheetDir(); + Gtk.Label lbl=new Gtk.Label("CueSheet Music Directory:"); + Gtk.FileChooserButton btn=new Gtk.FileChooserButton("CueSheet Music Directory:",Gtk.FileChooserAction.SelectFolder); + btn.SelectFilename (dir); + Gtk.HBox box=new Gtk.HBox(); + box.Add (lbl); + box.Add (btn); + box.ShowAll (); + + double d=src.getPbSize (); + Gtk.HScale hs=new Gtk.HScale(1.0,3.0,0.25); + hs.Value=d; + Gtk.Label lbl1=new Gtk.Label("Size of music icons:"); + Gtk.HBox box1=new Gtk.HBox(); + box1.Add (lbl1); + box1.Add (hs); + box1.ShowAll (); + + this.VBox.Add (box); + this.VBox.Add (box1); + this.AddButton (Gtk.Stock.Ok,1); + this.AddButton (Gtk.Stock.Cancel,2); + this.Title="CueSheets Preferences"; + int result=this.Run (); + if (result==1) { + dir=btn.Filename; + src.setCueSheetDir(dir); + src.setPbSize (hs.Value); + } + this.Destroy (); + } + } + + private class CustomView : ISourceContents + { + //Gtk.Button up; + Gtk.ListStore store; + Gtk.TreeView view; + //Gtk.Image img; + Gtk.VBox box; + string type="directory"; + CueSheet sheet; + //Gtk.Table table; + Gtk.ScrolledWindow scroll; + int index=-1; + private CueSheetsSource MySource=null; + + uint _position=0; + bool _set_position=false; + + const int IMG_NOIMAGE=0; + const int IMG_NOIMAGE_DIR=1; + const int IMG_DIRECTORY=2; + + Gdk.Pixbuf _img_dir=null; + Gdk.Pixbuf _img_playlist=null; + Gdk.Pixbuf _pb_size=null; + + + private Gdk.Pixbuf GetDefaultPixBuf(int type) { + Gdk.Pixbuf img=_img_playlist; + if (type==1000) { img=_pb_size; } + if (type==IMG_DIRECTORY) { + img=_img_dir; + } + return img; + } + + private int getPixbufWidth() { + return (int) (_pb_size.Width*MySource.getPbSize()); + } + + private int getPixbufHeight() { + return (int) (_pb_size.Height*MySource.getPbSize()); + } + + public CueSheet getSheet() { + return sheet; + } + + private String basedir=null; + private String cwdir=null; + + public string basename(string f) { + string bn=Regex.Replace (f,"^([^/]*[/])+",""); + return bn; + } + + public string cuename(string f) { + string cn=Regex.Replace(basename (f),"[.]cue$",""); + return cn; + } + + public void fill(Gtk.ListStore store) { + type="directory"; + store.Clear (); + if (basedir==null) { + basedir=MySource.getCueSheetDir (); + } + if (cwdir==null) { cwdir=basedir; } + string [] dirs=Directory.GetDirectories(cwdir, "*"); + string [] sheets=Directory.GetFiles (cwdir,"*.cue"); + int i=0; + foreach (string file in sheets) { + i=i+1; + string name=cuename(file); + CueSheet s=new CueSheet(file,cwdir); + Gdk.Pixbuf pb; + if (s.imageFileName ()!="") { + string imgf=cwdir+"/"+s.imageFileName (); + pb=new Gdk.Pixbuf(imgf,getPixbufWidth(),getPixbufHeight()); + } else { + pb=GetDefaultPixBuf(IMG_NOIMAGE_DIR); + } + store.AppendValues(i,pb,name); + } + foreach (string dir in dirs) { + string name=basename (dir); + if (name.Substring (0,1)!=".") { + i=i+1; + Gdk.Pixbuf pb=GetDefaultPixBuf(IMG_DIRECTORY); + store.AppendValues(i,pb,name); + } + } + } + + public void seekSong(int i) { + CueSheetEntry e=sheet.entry (i); + double offset=e.offset (); + //Console.WriteLine ("Offset="+offset+", current="+ServiceManager.PlayerEngine.Position+ + // ", msecs="+offset*1000.0 + // ); + //ServiceManager.PlayerEngine.SetNextTrack (e); + //ServiceManager.PlayerEngine.Open(e); + ServiceManager.PlayerEngine.SetCurrentTrack(e); + _position=(uint) (offset*1000.0); + _set_position=true; + mscount=chgcount-(1000/timeout); + //ServiceManager.PlayerEngine.PlayAt((uint) (offset*1000.0)); + //ServiceManager.PlayerEngine.Position=_position; + + } + + public void reLoad() { + index=0; + ServiceManager.PlayerEngine.SetAccurateSeek(true); + CueSheetEntry e=sheet.entry(index); + ServiceManager.PlayerEngine.Open (e); + ServiceManager.PlayerEngine.Play (); + //ServiceManager.PlayerEngine.PlayAt ((uint) (e.offset ()*1000.0)); + if (ServiceManager.PlaybackController.Source!=MySource) { + ServiceManager.PlaybackController.Source=MySource; + } + if (ServiceManager.PlaybackController.NextSource!=MySource) { + ServiceManager.PlaybackController.NextSource=MySource; + } + ServiceManager.PlaybackController.SetSeekMode (true); + mscount=chgcount-1; + } + + // Every N ms + private int timeout=100; + private int mscount=0; + private int chgcount=3000/100; // every 3 seconds + + public bool PositionDisplay() { + if (ServiceManager.PlaybackController.Source==MySource) { + if (sheet!=null) { + mscount+=1; + if (mscount>chgcount) { mscount=0; } + + // Position if necessary + if (_set_position) { + _set_position=false; + ServiceManager.PlayerEngine.Position=_position; + } + + // Track number + uint pos=ServiceManager.PlayerEngine.Position; + double p=((double) pos)/1000.0; + int i=sheet.searchIndex(p); + //Console.WriteLine ("Position="+p+", i="+i+", index="+index); + if (i!=index && i>=0) { + // Handle repeat track + if (ServiceManager.PlaybackController.RepeatMode==PlaybackRepeatMode.RepeatSingle) { + seekSong (index); + } else { + // Every 2 seconds + if (mscount==0) { + index=i; + CueSheetEntry e=sheet.entry(index); + ServiceManager.PlayerEngine.SetCurrentTrack (e); + } + } + } + + if (type=="cuesheet" && mscount==0) { + int [] idx=new int[1]; + idx[0]=index; + + Gtk.TreePath path=new Gtk.TreePath(idx); + Gtk.TreeViewColumn c=new Gtk.TreeViewColumn(); + Gtk.TreePath pp; + view.GetCursor (out pp,out c); + if (pp==null || pp.Indices[0]!=index) { + view.SetCursor (path,null,false); + } + } + + } + } + return true; + } + + public Boolean next() { + if (sheet==null) { return false; } + if (index<sheet.nEntries ()-1) { + index+=1; + } else { + index=0; + } + seekSong(index); + return true; + } + + public Boolean previous() { + if (sheet==null) { return false; } + if (index>0) { + index-=1; + } else { + index=sheet.nEntries ()-1; + } + seekSong(index); + return true; + } + + public void loadCueSheet(string file,Gtk.ListStore store) { + type="cuesheet"; + //Console.WriteLine ("loadCueSheet:"+file); + store.Clear (); + sheet=new CueSheet(file,cwdir); + Gdk.Pixbuf pb; + /*if (sheet.imageFileName ()!="") { + string imgf=cwdir+"/"+sheet.imageFileName (); + pb=new Gdk.Pixbuf(imgf,getPixbufWidth(),getPixbufHeight()); + } else {*/ + pb=GetDefaultPixBuf(IMG_NOIMAGE); + /*}*/ + int i=0; + for(i=0;i<sheet.nEntries ();i++) { + store.AppendValues (i+1,pb,sheet.entry (i).title ()); + } + //Console.WriteLine (type+", image="+sheet.imageFileName ()); + + /*if (File.Exists (imgf)) { + Gdk.Rectangle a=img.Allocation; + Gdk.Pixbuf pb=new Gdk.Pixbuf(imgf,a.Width,a.Height); + img.Pixbuf=pb; + }*/ + reLoad (); + } + + public CustomView(CueSheetsSource ms) { + MySource=ms; + { + Gtk.Label w=new Gtk.Label(); + _pb_size=w.RenderIcon (Gtk.Stock.Index.ToString (),Gtk.IconSize.Button,null); + _img_dir=w.RenderIcon (Gtk.Stock.Directory.ToString (),Gtk.IconSize.Button,null); + _img_playlist=w.RenderIcon(Gtk.Stock.Index.ToString (),Gtk.IconSize.Button,null); + } + + + store = new Gtk.ListStore(typeof(int),typeof(Gdk.Pixbuf),typeof(string)); + view = new Gtk.TreeView(); + Gtk.CellRendererPixbuf cpb=new Gtk.CellRendererPixbuf(); + view.AppendColumn ("Nr.", new Gtk.CellRendererText (), "text", 0); + view.AppendColumn ("Icon.",cpb,"pixbuf",1); + view.AppendColumn ("Track/Album", new Gtk.CellRendererText (), "text", 2); + view.CursorChanged += new EventHandler(EvtCursorChanged); + view.RowActivated += new Gtk.RowActivatedHandler(EvtRowActivated); + view.Model = store; + scroll=new Gtk.ScrolledWindow(); + scroll.Add (view); + + Gtk.Toolbar bar=new Gtk.Toolbar(); + + Gtk.Image icn_up=new Gtk.Image(Gtk.Stock.GoUp,Gtk.IconSize.Button); + Gtk.Button up=new Gtk.Button(icn_up); + up.Clicked+=new EventHandler(handleUp); + bar.Add (up); + + Gtk.Image icn_set=new Gtk.Image(Gtk.Stock.Preferences,Gtk.IconSize.Button); + Gtk.Button prefs=new Gtk.Button(icn_set); + prefs.Clicked+=new EventHandler(handleSet); + bar.Add (prefs); + + Gtk.Image icn_about=new Gtk.Image(Gtk.Stock.About,Gtk.IconSize.Button); + Gtk.Button about=new Gtk.Button(icn_about); + about.Clicked+=new EventHandler(handleAbout); + bar.Add (about); + + fill(store); + box = new Gtk.VBox(); + box.PackStart (bar,false,true,0); + box.PackStart(scroll); + box.ShowAll(); + + GLib.Timeout.Add ((uint) timeout,(GLib.TimeoutHandler) PositionDisplay); + } + + public void handleUp(object sender,EventArgs a) { + if (cwdir==basedir) { + // do nothing + } else { + if (type!="cuesheet") { + cwdir=Regex.Replace (cwdir,"[/][^/]*$",""); + } + fill (store); + } + } + + public void handleSet(object sender,EventArgs a) { + MySource.OnConfigure (sender,a); + string d=MySource.getCueSheetDir (); + if (basedir!=d) { + basedir=d; + cwdir=d; + fill (store); + } + } + + public void handleAbout(object sender,EventArgs a) { + Gtk.AboutDialog ab=new Gtk.AboutDialog(); + ab.Title="About the CueSheets extension"; + ab.Authors=new string[] {"Hans Oesterholt"}; + ab.Authors[0]="Hans Oesterholt"; + ab.Version="0.0.1"; + ab.Comments="CueSheets is an extension that allows you to play music from cuesheets in banshee"; + ab.Website="http://oesterholt.net?env=data&page=banshee-cuesheets"; + ab.Run (); + ab.Destroy (); + } + + public void EvtCursorChanged(object sender,EventArgs a) { + mscount=0; // Reset cursor change timer + } + + public void EvtRowActivated(object sender, Gtk.RowActivatedArgs a) { + Gtk.TreeSelection selection = (sender as Gtk.TreeView).Selection; + Gtk.TreeModel model; + Gtk.TreeIter iter; + // THE ITER WILL POINT TO THE SELECTED ROW + Console.WriteLine ("source="+ServiceManager.PlaybackController.Source); + Console.WriteLine ("this="+this); + if (selection.GetSelected(out model, out iter)) { + if (this.type=="cuesheet") { + if (ServiceManager.PlaybackController.Source != MySource) { + reLoad (); + //this.type=""; + //EvtRowActivated (sender,a); + } + int track=(int) model.GetValue (iter,0); + int i=track-1; + seekSong (i); + index=i; + } else { + string name=model.GetValue (iter,2).ToString (); + string file=cwdir+"/"+name; + string cuef=file+".cue"; + Console.WriteLine ("File="+file); + Console.WriteLine ("CueF="+cuef); + if (Directory.Exists (file)) { + Console.WriteLine ("Directory"); + cwdir=file; + fill (store); + } else { + if (File.Exists (cuef)) { + Console.WriteLine ("CueSheet"); + loadCueSheet(cuef,store); + } + } + } + } + } + + + public bool SetSource (ISource source) { return true; } + public void ResetSource () { } + public Gtk.Widget Widget { get { return box; } } + public ISource Source { get { return null; } } + } + + } +} diff --git a/src/CueSheets/CueSheets.addin.xml b/src/CueSheets/CueSheets.addin.xml new file mode 100644 index 0000000..9d52bc9 --- /dev/null +++ b/src/CueSheets/CueSheets.addin.xml @@ -0,0 +1,84 @@ +<?xml version="1.0" encoding="utf-8"?> +<Addin + id="Banshee.CueSheets" + version="1.0" + compatVersion="1.0" + copyright="Copyright © 2013 Hans Oesterholt. Licensed under the MIT X11 license." + name="CueSheet support for Banshee" + category="Community Extensions" + description="Support for playback of audio files with cuesheets" + author="Hans Oesterholt" + url="http://oesterholt.net" + defaultEnabled="true"> + + <Localizer type="Gettext" catalog="banshee-community-extensions" location="../../../share/locale"/> + + <Dependencies> + <Addin id="Banshee.Services" version="1.0"/> + <Addin id="Banshee.ThickClient" version="1.0"/> + </Dependencies> + + <!-- Extensions are loaded by Banshee via Mono.Addins by extending a + particular ExtensionPoint. + + There is a very general ExtensionPoint called + /Banshee/ServiceManager/Service, and there are several much more + specific ones. A Service extension will be instantiated when Banshee + starts, and can do whatever it wants with the Banshee API. Where an + ImportSource extension will be loaded when the user opens the Import + Dialog. + + Here is an example of how to define an extension point: + + <Extension path="ExtensionPointPath"> + <ExtensionPointType class="Banshee.CueSheets.CueSheetsExtensionPointType"/> + </Extension> + + An extension must have one or more such definitions. + + Here are the ExtensionPoints in Banshee: + + Path: /Banshee/ServiceManager/Service + Type: Service + Desc: General purpose, run any code, instantiated at startup + + Path: /Banshee/SourceManager/Source + Type: Source + Desc: Create a Source in the source list + + Path: /Banshee/Library/ImportSource + Type: ImportSource + Desc: Add an entry to the Import Dialog dropdown. + + Path: /Banshee/Gui/TrackEditor/NotebookPage + Type: TrackEditorPage + Desc: Add a tab ("page") to the Edit Track Information or Properties + dialogs. + + Path: /Banshee/Gui/TrackEditor/Modifier + Type: Modifier + Desc: Defines an extension for the track editor that can modify it in + some way. + + Path: /Banshee/ThickClient/ContextPane + Type: ContextPage + Desc: Defines a new GTK+ context page, for showing contextual + information beneath the main track source view. + + There are quite a few other ExtensionPoints, but they are mostly related to + providing platform-specific backends for hardware integration and the like. + --> + + <!-- Define a Service extension. This will instantiate the specified class on startup, + letting us do whatever we want. + <Extension path="/Banshee/ServiceManager/Service"> + <Service class="Banshee.CueSheets.CueSheetsService"/> + </Extension>--> + + <!-- Define a Source extension. This will instantiate a new Source object, + listed in the left-most Source TreeView. --> + <Extension path="/Banshee/SourceManager/Source"> + <Source class="Banshee.CueSheets.CueSheetsSource"/> + </Extension> + +</Addin> diff --git a/src/CueSheets/CueSheets.csproj b/src/CueSheets/CueSheets.csproj new file mode 100644 index 0000000..81f6daa --- /dev/null +++ b/src/CueSheets/CueSheets.csproj @@ -0,0 +1,109 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0"> + <PropertyGroup> + <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> + <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> + <ProductVersion>8.0.50727</ProductVersion> + <ProjectGuid>{}</ProjectGuid> + <OutputType>Library</OutputType> + <UseParentDirectoryAsNamespace>true</UseParentDirectoryAsNamespace> + <AssemblyName>Banshee.CueSheets</AssemblyName> + <SchemaVersion>2.0</SchemaVersion> + <Optimize>true</Optimize> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + <CheckForOverflowUnderflow>true</CheckForOverflowUnderflow> + <RootNamespace>Banshee.CueSheets</RootNamespace> + <AssemblyOriginatorKeyFile>.</AssemblyOriginatorKeyFile> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> + <DebugSymbols>true</DebugSymbols> + <DebugType>full</DebugType> + <OutputPath>..\..\..\bin</OutputPath> + <CustomCommands> + <CustomCommands> + <Command type="Execute" command="make run" workingdir="${SolutionDir}" /> + </CustomCommands> + </CustomCommands> + <Optimize>false</Optimize> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Windows|AnyCPU' "> + <DebugSymbols>true</DebugSymbols> + <DebugType>full</DebugType> + <OutputPath>..\..\..\bin</OutputPath> + <PlatformTarget>x86</PlatformTarget> + <Optimize>false</Optimize> + </PropertyGroup> + <ItemGroup> + <Reference Include="System" /> + <Reference Include="gtk-sharp, Version=2.12.0.0, Culture=neutral, PublicKeyToken=35e10195dab3c99f"> + <Package>gtk-sharp-2.0</Package> + </Reference> + <Reference Include="Mono.Posix" /> + <Reference Include="gdk-sharp, Version=2.12.0.0, Culture=neutral, PublicKeyToken=35e10195dab3c99f"> + <Package>gtk-sharp-2.0</Package> + </Reference> + <Reference Include="Mono.Cairo" /> + <Reference Include="Mono.Addins"> + <Package>mono-addins</Package> + </Reference> + <Reference Include="Hyena, Version=1.7.0.0, Culture=neutral"> + <Package>banshee-hyena</Package> + </Reference> + <Reference Include="Hyena.Data.Sqlite, Version=1.7.0.0, Culture=neutral"> + <Package>banshee-hyena-data-sqlite</Package> + </Reference> + <Reference Include="Hyena.Gui, Version=1.7.0.0, Culture=neutral"> + <Package>banshee-hyena-gui</Package> + </Reference> + <Reference Include="System.Core" /> + <Reference Include="Banshee.Services"> + <HintPath>..\..\..\..\..\..\usr\local\banshee\lib\banshee\Banshee.Services.dll</HintPath> + </Reference> + <Reference Include="Banshee.CollectionIndexer"> + <HintPath>..\..\..\..\..\..\usr\local\banshee\lib\banshee\Banshee.CollectionIndexer.dll</HintPath> + </Reference> + <Reference Include="Banshee.Core"> + <HintPath>..\..\..\..\..\..\usr\local\banshee\lib\banshee\Banshee.Core.dll</HintPath> + </Reference> + <Reference Include="Banshee.ThickClient"> + <HintPath>..\..\..\..\..\..\usr\local\banshee\lib\banshee\Banshee.ThickClient.dll</HintPath> + </Reference> + <Reference Include="Banshee.WebBrowser"> + <HintPath>..\..\..\..\..\..\usr\local\banshee\lib\banshee\Banshee.WebBrowser.dll</HintPath> + </Reference> + <Reference Include="Banshee.Widgets"> + <HintPath>..\..\..\..\..\..\usr\local\banshee\lib\banshee\Banshee.Widgets.dll</HintPath> + </Reference> + <Reference Include="glib-sharp, Version=2.12.0.0, Culture=neutral, PublicKeyToken=35e10195dab3c99f"> + <Private>False</Private> + <Package>glib-sharp-2.0</Package> + </Reference> + </ItemGroup> + <ItemGroup> + <EmbeddedResource Include="CueSheets.addin.xml"> + <LogicalName>CueSheets.addin.xml</LogicalName> + </EmbeddedResource> + </ItemGroup> + <ItemGroup> + <Compile Include="Banshee.CueSheets\CueSheetsSource.cs" /> + <Compile Include="Banshee.CueSheets\CueSheet.cs" /> + <Compile Include="Banshee.CueSheets\CueSheetEntry.cs" /> + </ItemGroup> + <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> + <ProjectExtensions> + <MonoDevelop> + <Properties> + <MonoDevelop.Autotools.MakefileInfo IntegrationEnabled="true" RelativeMakefileName="Makefile.am"> + <BuildFilesVar Sync="true" Name="SOURCES" /> + <DeployFilesVar /> + <ResourcesVar Sync="true" Name="RESOURCES" /> + <OthersVar /> + <GacRefVar /> + <AsmRefVar /> + <ProjectRefVar /> + </MonoDevelop.Autotools.MakefileInfo> + </Properties> + </MonoDevelop> + </ProjectExtensions> +</Project> diff --git a/src/CueSheets/Makefile.am b/src/CueSheets/Makefile.am new file mode 100644 index 0000000..bc4e196 --- /dev/null +++ b/src/CueSheets/Makefile.am @@ -0,0 +1,11 @@ +ASSEMBLY = Banshee.CueSheets +LINK = $(BANSHEE_LIBS) + +SOURCES = \ + Banshee.CueSheets/CueSheet.cs \ + Banshee.CueSheets/CueSheetEntry.cs \ + Banshee.CueSheets/CueSheetsSource.cs + +RESOURCES = CueSheets.addin.xml + +include $(top_srcdir)/build/build.mk diff --git a/src/CueSheets/ThemeIcons/128x128/categories/cueplay.png b/src/CueSheets/ThemeIcons/128x128/categories/cueplay.png Binary files differnew file mode 100644 index 0000000..e41c0ab --- /dev/null +++ b/src/CueSheets/ThemeIcons/128x128/categories/cueplay.png diff --git a/src/CueSheets/ThemeIcons/22x22/categories/cueplay.png b/src/CueSheets/ThemeIcons/22x22/categories/cueplay.png Binary files differnew file mode 100644 index 0000000..0597905 --- /dev/null +++ b/src/CueSheets/ThemeIcons/22x22/categories/cueplay.png diff --git a/src/CueSheets/ThemeIcons/32x32/categories/cueplay.png b/src/CueSheets/ThemeIcons/32x32/categories/cueplay.png Binary files differnew file mode 100644 index 0000000..a4b470a --- /dev/null +++ b/src/CueSheets/ThemeIcons/32x32/categories/cueplay.png diff --git a/src/CueSheets/ThemeIcons/48x48/categories/cueplay.png b/src/CueSheets/ThemeIcons/48x48/categories/cueplay.png Binary files differnew file mode 100644 index 0000000..ae76eb8 --- /dev/null +++ b/src/CueSheets/ThemeIcons/48x48/categories/cueplay.png diff --git a/src/CueSheets/ThemeIcons/64x64/categories/cueplay.png b/src/CueSheets/ThemeIcons/64x64/categories/cueplay.png Binary files differnew file mode 100644 index 0000000..b79a3e0 --- /dev/null +++ b/src/CueSheets/ThemeIcons/64x64/categories/cueplay.png diff --git a/src/Makefile.am b/src/Makefile.am index e80fff0..dbdee7e 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,4 +1,5 @@ SUBDIRS = \ + CueSheets \ AlarmClock \ AlbumArtWriter \ Ampache \ |