├── MBGmusicSync.suo
├── Screenshot.png
├── Plugin
├── mb_GoogleMusicSync_v1.zip
├── mb_GoogleMusicSync_v1.5.zip
├── mb_GoogleMusicSync_v1.7.zip
└── mb_GoogleMusicSync_v1.6wLogging.zip
├── MBGmusic
├── app.config
├── Models
│ ├── MbPlaylist.cs
│ └── MbSong.cs
├── packages.config
├── MBGmusic.csproj.user
├── Properties
│ └── AssemblyInfo.cs
├── Plugin
│ ├── Logger.cs
│ ├── PlaylistSync.cs
│ ├── Settings.cs
│ ├── GMusicSyncData.cs
│ ├── MBGmusicPlugin.cs
│ ├── MbSyncData.cs
│ └── MusicBeeInterface.cs
├── Configure.resx
├── MBGmusic.csproj
├── Configure.cs
└── Configure.Designer.cs
├── TestGMusicAPI
├── app.config
├── Properties
│ ├── Settings.settings
│ ├── Settings.Designer.cs
│ ├── AssemblyInfo.cs
│ ├── Resources.Designer.cs
│ └── Resources.resx
├── Program.cs
├── TestGMusicAPI.csproj
├── Form1.cs
├── Form1.resx
└── Form1.Designer.cs
├── TestPlaylistCreation
├── TestWindow.cs
├── Properties
│ └── AssemblyInfo.cs
├── TestMBApi.csproj
├── TestWindow.Designer.cs
├── TestWindow.resx
└── MBTestAPIPlugin.cs
├── .gitignore
├── readme.md
└── MBGmusicSync.sln
/MBGmusicSync.suo:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/leoedin/MBGmusicSync/HEAD/MBGmusicSync.suo
--------------------------------------------------------------------------------
/Screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/leoedin/MBGmusicSync/HEAD/Screenshot.png
--------------------------------------------------------------------------------
/Plugin/mb_GoogleMusicSync_v1.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/leoedin/MBGmusicSync/HEAD/Plugin/mb_GoogleMusicSync_v1.zip
--------------------------------------------------------------------------------
/Plugin/mb_GoogleMusicSync_v1.5.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/leoedin/MBGmusicSync/HEAD/Plugin/mb_GoogleMusicSync_v1.5.zip
--------------------------------------------------------------------------------
/Plugin/mb_GoogleMusicSync_v1.7.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/leoedin/MBGmusicSync/HEAD/Plugin/mb_GoogleMusicSync_v1.7.zip
--------------------------------------------------------------------------------
/Plugin/mb_GoogleMusicSync_v1.6wLogging.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/leoedin/MBGmusicSync/HEAD/Plugin/mb_GoogleMusicSync_v1.6wLogging.zip
--------------------------------------------------------------------------------
/MBGmusic/app.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/TestGMusicAPI/app.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/TestGMusicAPI/Properties/Settings.settings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/MBGmusic/Models/MbPlaylist.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 |
6 | namespace MusicBeePlugin.Models
7 | {
8 | public class MbPlaylist
9 | {
10 | public String mbName;
11 | public String Name;
12 | public override string ToString()
13 | {
14 | return Name;
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/MBGmusic/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/MBGmusic/Models/MbSong.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 |
6 | namespace MusicBeePlugin.Models
7 | {
8 | public class MbSong
9 | {
10 | public String Filename;
11 | public String Artist;
12 | public String Title;
13 |
14 | public override string ToString()
15 | {
16 | return Artist + " - " + Title;
17 | }
18 |
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/TestGMusicAPI/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Windows.Forms;
5 |
6 | namespace TestGMusicAPI
7 | {
8 | static class Program
9 | {
10 | ///
11 | /// The main entry point for the application.
12 | ///
13 | [STAThread]
14 | static void Main()
15 | {
16 | Application.EnableVisualStyles();
17 | Application.SetCompatibleTextRenderingDefault(false);
18 | Application.Run(new Form1());
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/MBGmusic/MBGmusic.csproj.user:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Program
5 | C:\Program Files (x86)\MusicBee\MusicBee.exe
6 |
7 |
8 | Program
9 | C:\Program Files (x86)\MusicBee\MusicBee.exe
10 |
11 |
12 | Program
13 | C:\Program Files (x86)\MusicBee\MusicBee.exe
14 |
15 |
--------------------------------------------------------------------------------
/TestGMusicAPI/Properties/Settings.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // This code was generated by a tool.
4 | // Runtime Version:4.0.30319.42000
5 | //
6 | // Changes to this file may cause incorrect behavior and will be lost if
7 | // the code is regenerated.
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace TestGMusicAPI.Properties {
12 |
13 |
14 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
15 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "14.0.0.0")]
16 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
17 |
18 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
19 |
20 | public static Settings Default {
21 | get {
22 | return defaultInstance;
23 | }
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/TestPlaylistCreation/TestWindow.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.ComponentModel;
4 | using System.Data;
5 | using System.Drawing;
6 | using System.Linq;
7 | using System.Text;
8 | using System.Windows.Forms;
9 |
10 | namespace TestPlaylistCreation
11 | {
12 | public partial class TestWindow : Form
13 | {
14 | private MusicBeePlugin.Plugin.MusicBeeApiInterface _mbApiInterface;
15 | public TestWindow(MusicBeePlugin.Plugin.MusicBeeApiInterface api)
16 | {
17 | InitializeComponent();
18 | _mbApiInterface = api;
19 | }
20 |
21 | private void CreatePlaylist_button_Click(object sender, EventArgs e)
22 | {
23 | string dir = CreatePlaylistDirectory_tb.Text;
24 | string name = CreatePlaylistName_tb.Text;
25 |
26 | string[] files = null;
27 | if (_mbApiInterface.Library_QueryFiles("domain=library"))
28 | {
29 | // Old (deprecated)
30 | //public char[] filesSeparators = { '\0' };
31 | //files = _mbApiInterface.Library_QueryGetAllFiles().Split(filesSeparators, StringSplitOptions.RemoveEmptyEntries);
32 | _mbApiInterface.Library_QueryFilesEx("domain=library", ref files);
33 | }
34 |
35 | if (dir == null)
36 | dir = "";
37 |
38 | _mbApiInterface.Playlist_CreatePlaylist(dir, name, files);
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/TestGMusicAPI/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyTitle("TestGMusicAPI")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("")]
12 | [assembly: AssemblyProduct("TestGMusicAPI")]
13 | [assembly: AssemblyCopyright("Copyright © 2013")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Setting ComVisible to false makes the types in this assembly not visible
18 | // to COM components. If you need to access a type in this assembly from
19 | // COM, set the ComVisible attribute to true on that type.
20 | [assembly: ComVisible(false)]
21 |
22 | // The following GUID is for the ID of the typelib if this project is exposed to COM
23 | [assembly: Guid("d5a99921-cbe0-4a64-b0ef-d382bd3a5515")]
24 |
25 | // Version information for an assembly consists of the following four values:
26 | //
27 | // Major Version
28 | // Minor Version
29 | // Build Number
30 | // Revision
31 | //
32 | // You can specify all the values or you can default the Build and Revision Numbers
33 | // by using the '*' as shown below:
34 | // [assembly: AssemblyVersion("1.0.*")]
35 | [assembly: AssemblyVersion("1.0.0.0")]
36 | [assembly: AssemblyFileVersion("1.0.0.0")]
37 |
--------------------------------------------------------------------------------
/MBGmusic/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyTitle("MusicBeeGoogleMusicPlaylistSync")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("Leo Rampen")]
12 | [assembly: AssemblyProduct("MbGMusicSync")]
13 | [assembly: AssemblyCopyright("Copyright © 2015")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Setting ComVisible to false makes the types in this assembly not visible
18 | // to COM components. If you need to access a type in this assembly from
19 | // COM, set the ComVisible attribute to true on that type.
20 | [assembly: ComVisible(false)]
21 |
22 | // The following GUID is for the ID of the typelib if this project is exposed to COM
23 | [assembly: Guid("c1acdbd8-6b22-4807-bba3-d0237ccd74c1")]
24 |
25 | // Version information for an assembly consists of the following four values:
26 | //
27 | // Major Version
28 | // Minor Version
29 | // Build Number
30 | // Revision
31 | //
32 | // You can specify all the values or you can default the Build and Revision Numbers
33 | // by using the '*' as shown below:
34 | // [assembly: AssemblyVersion("1.0.*")]
35 | [assembly: AssemblyVersion("1.5.0.0")]
36 | [assembly: AssemblyFileVersion("1.5.0.0")]
37 |
--------------------------------------------------------------------------------
/TestPlaylistCreation/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyTitle("TestPlaylistCreation")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("")]
12 | [assembly: AssemblyProduct("TestPlaylistCreation")]
13 | [assembly: AssemblyCopyright("Copyright © 2015")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Setting ComVisible to false makes the types in this assembly not visible
18 | // to COM components. If you need to access a type in this assembly from
19 | // COM, set the ComVisible attribute to true on that type.
20 | [assembly: ComVisible(false)]
21 |
22 | // The following GUID is for the ID of the typelib if this project is exposed to COM
23 | [assembly: Guid("a1dd2d31-feb5-4cb8-a2e5-596517baf1e8")]
24 |
25 | // Version information for an assembly consists of the following four values:
26 | //
27 | // Major Version
28 | // Minor Version
29 | // Build Number
30 | // Revision
31 | //
32 | // You can specify all the values or you can default the Build and Revision Numbers
33 | // by using the '*' as shown below:
34 | // [assembly: AssemblyVersion("1.0.*")]
35 | [assembly: AssemblyVersion("1.0.0.0")]
36 | [assembly: AssemblyFileVersion("1.0.0.0")]
37 |
--------------------------------------------------------------------------------
/MBGmusic/Plugin/Logger.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.IO;
6 |
7 | namespace MusicBeePlugin
8 | {
9 | class Logger
10 | {
11 | private List> _log;
12 |
13 | public EventHandler OnLogUpdated;
14 |
15 | public static Logger Instance
16 | {
17 | get
18 | {
19 | if (_instance == null)
20 | _instance = new Logger();
21 | return _instance;
22 | }
23 | }
24 |
25 | private static Logger _instance;
26 |
27 | private Logger()
28 | {
29 | _log = new List>();
30 | logToFile = false;
31 | }
32 |
33 | private String _logFile;
34 | private bool logToFile;
35 | public String LogFile
36 | {
37 | get
38 | {
39 | return _logFile;
40 | }
41 | set
42 | {
43 | _logFile = value;
44 | logToFile = true;
45 | }
46 | }
47 |
48 |
49 |
50 | public void Log(string text)
51 | {
52 | _log.Add(new Tuple(DateTime.Now, text));
53 |
54 | if (OnLogUpdated != null)
55 | OnLogUpdated(this, new EventArgs());
56 |
57 | }
58 |
59 | public void DebugLog(string text)
60 | {
61 | if (logToFile)
62 | {
63 | using (StreamWriter w = File.AppendText(LogFile))
64 | {
65 | w.WriteLine(text + "\r\n");
66 | }
67 | }
68 | }
69 |
70 | public List> LogData { get { return _log; } }
71 |
72 | public string LastLog
73 | {
74 | get
75 | {
76 | if (_log.Count > 0)
77 | {
78 | return _log[_log.Count - 1].Item2;
79 | }
80 | else
81 | {
82 | return "";
83 | }
84 | }
85 | }
86 |
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/MBGmusic/Plugin/PlaylistSync.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using MusicBeePlugin.Models;
6 | using System.Threading;
7 | using GooglePlayMusicAPI;
8 |
9 | namespace MusicBeePlugin
10 | {
11 | class PlaylistSync
12 | {
13 | private Logger log;
14 |
15 | private Settings _settings;
16 |
17 | private Plugin.MusicBeeApiInterface _mbApiInterface;
18 |
19 | public PlaylistSync(Settings settings, Plugin.MusicBeeApiInterface mbApiInterface)
20 | {
21 | _settings = settings;
22 |
23 | _mbApiInterface = mbApiInterface;
24 |
25 | log = Logger.Instance;
26 |
27 | _gMusic = new GMusicSyncData(settings, mbApiInterface);
28 | _mbSync = new MbSyncData(settings, mbApiInterface);
29 | }
30 |
31 | private GMusicSyncData _gMusic;
32 | public GMusicSyncData GMusic { get { return _gMusic; } }
33 |
34 | private MbSyncData _mbSync;
35 | public MbSyncData MBSync { get { return _mbSync; } }
36 |
37 | ///
38 | /// This is blocking, so run it on a thread
39 | ///
40 | public async void SyncPlaylists()
41 | {
42 | if (!_gMusic.LoggedIn || _gMusic.SyncRunning)
43 | return;
44 |
45 | if (_settings.SyncLocalToRemote)
46 | {
47 | List playlists = new List();
48 | List allPlaylists = _mbSync.GetMbPlaylists();
49 | // Only sync the playlists that the settings say we should
50 | // Surely there's a nicer LINQ query for this?
51 | foreach (MbPlaylist pls in allPlaylists)
52 | {
53 | if (_settings.MBPlaylistsToSync.Contains(pls.mbName))
54 | {
55 | playlists.Add(pls);
56 | }
57 | }
58 | _gMusic.SyncPlaylistsToGMusic(playlists);
59 | }
60 | else
61 | {
62 | if (_gMusic.DataFetched)
63 | {
64 | List playlists = new List();
65 | foreach (string id in _settings.GMusicPlaylistsToSync)
66 | {
67 | Playlist pls = _gMusic.AllPlaylists.FirstOrDefault(p => p.ID == id);
68 | if (pls != null)
69 | playlists.Add(pls);
70 | }
71 | _mbSync.SyncPlaylistsToMusicBee(playlists, _gMusic.AllSongs);
72 | return;
73 | } else {
74 | return;
75 | }
76 | }
77 |
78 | }
79 |
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/TestGMusicAPI/Properties/Resources.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // This code was generated by a tool.
4 | // Runtime Version:4.0.30319.42000
5 | //
6 | // Changes to this file may cause incorrect behavior and will be lost if
7 | // the code is regenerated.
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace TestGMusicAPI.Properties {
12 | using System;
13 |
14 |
15 | ///
16 | /// A strongly-typed resource class, for looking up localized strings, etc.
17 | ///
18 | // This class was auto-generated by the StronglyTypedResourceBuilder
19 | // class via a tool like ResGen or Visual Studio.
20 | // To add or remove a member, edit your .ResX file then rerun ResGen
21 | // with the /str option, or rebuild your VS project.
22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
25 | internal class Resources {
26 |
27 | private static global::System.Resources.ResourceManager resourceMan;
28 |
29 | private static global::System.Globalization.CultureInfo resourceCulture;
30 |
31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
32 | internal Resources() {
33 | }
34 |
35 | ///
36 | /// Returns the cached ResourceManager instance used by this class.
37 | ///
38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
39 | internal static global::System.Resources.ResourceManager ResourceManager {
40 | get {
41 | if (object.ReferenceEquals(resourceMan, null)) {
42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("TestGMusicAPI.Properties.Resources", typeof(Resources).Assembly);
43 | resourceMan = temp;
44 | }
45 | return resourceMan;
46 | }
47 | }
48 |
49 | ///
50 | /// Overrides the current thread's CurrentUICulture property for all
51 | /// resource lookups using this strongly typed resource class.
52 | ///
53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
54 | internal static global::System.Globalization.CultureInfo Culture {
55 | get {
56 | return resourceCulture;
57 | }
58 | set {
59 | resourceCulture = value;
60 | }
61 | }
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/TestPlaylistCreation/TestMBApi.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Debug
5 | AnyCPU
6 | 8.0.30703
7 | 2.0
8 | {4D303419-70E9-4989-933D-9BDF8CBA87CA}
9 | Library
10 | Properties
11 | TestPlaylistCreation
12 | mb_TestPlaylistCreation
13 | v4.0
14 | 512
15 |
16 |
17 | true
18 | full
19 | false
20 | bin\Debug\
21 | DEBUG;TRACE
22 | prompt
23 | 4
24 |
25 |
26 | pdbonly
27 | true
28 | bin\Release\
29 | TRACE
30 | prompt
31 | 4
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 | Form
50 |
51 |
52 | TestWindow.cs
53 |
54 |
55 |
56 |
57 | TestWindow.cs
58 |
59 |
60 |
61 |
62 | copy /Y "$(TargetDir)\*.*" "C:\Program Files (x86)\MusicBee\Plugins\"
63 |
64 |
71 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | Secret.cs
2 | packages/
3 |
4 | #################
5 | ## Eclipse
6 | #################
7 |
8 | *.pydevproject
9 | .project
10 | .metadata
11 | bin/
12 | tmp/
13 | *.tmp
14 | *.bak
15 | *.swp
16 | *~.nib
17 | local.properties
18 | .classpath
19 | .settings/
20 | .loadpath
21 |
22 | # External tool builders
23 | .externalToolBuilders/
24 |
25 | # Locally stored "Eclipse launch configurations"
26 | *.launch
27 |
28 | # CDT-specific
29 | .cproject
30 |
31 | # PDT-specific
32 | .buildpath
33 |
34 |
35 | #################
36 | ## Visual Studio
37 | #################
38 |
39 | ## Ignore Visual Studio temporary files, build results, and
40 | ## files generated by popular Visual Studio add-ons.
41 |
42 | # User-specific files
43 | *.suo
44 | *.user
45 | *.sln.docstates
46 |
47 | # Build results
48 |
49 | [Dd]ebug/
50 | [Rr]elease/
51 | x64/
52 | build/
53 | [Bb]in/
54 | [Oo]bj/
55 | Secret.cs
56 |
57 | # MSTest test Results
58 | [Tt]est[Rr]esult*/
59 | [Bb]uild[Ll]og.*
60 |
61 | *_i.c
62 | *_p.c
63 | *.ilk
64 | *.meta
65 | *.obj
66 | *.pch
67 | *.pdb
68 | *.pgc
69 | *.pgd
70 | *.rsp
71 | *.sbr
72 | *.tlb
73 | *.tli
74 | *.tlh
75 | *.tmp
76 | *.tmp_proj
77 | *.log
78 | *.vspscc
79 | *.vssscc
80 | .builds
81 | *.pidb
82 | *.log
83 | *.scc
84 |
85 | # Visual C++ cache files
86 | ipch/
87 | *.aps
88 | *.ncb
89 | *.opensdf
90 | *.sdf
91 | *.cachefile
92 |
93 | # Visual Studio profiler
94 | *.psess
95 | *.vsp
96 | *.vspx
97 |
98 | # Guidance Automation Toolkit
99 | *.gpState
100 |
101 | # ReSharper is a .NET coding add-in
102 | _ReSharper*/
103 | *.[Rr]e[Ss]harper
104 |
105 | # TeamCity is a build add-in
106 | _TeamCity*
107 |
108 | # DotCover is a Code Coverage Tool
109 | *.dotCover
110 |
111 | # NCrunch
112 | *.ncrunch*
113 | .*crunch*.local.xml
114 |
115 | # Installshield output folder
116 | [Ee]xpress/
117 |
118 | # DocProject is a documentation generator add-in
119 | DocProject/buildhelp/
120 | DocProject/Help/*.HxT
121 | DocProject/Help/*.HxC
122 | DocProject/Help/*.hhc
123 | DocProject/Help/*.hhk
124 | DocProject/Help/*.hhp
125 | DocProject/Help/Html2
126 | DocProject/Help/html
127 |
128 | # Click-Once directory
129 | publish/
130 |
131 | # Publish Web Output
132 | *.Publish.xml
133 | *.pubxml
134 | *.publishproj
135 |
136 | # NuGet Packages Directory
137 | ## TODO: If you have NuGet Package Restore enabled, uncomment the next line
138 | #packages/
139 |
140 | # Windows Azure Build Output
141 | csx
142 | *.build.csdef
143 |
144 | # Windows Store app package directory
145 | AppPackages/
146 |
147 | # Others
148 | sql/
149 | *.Cache
150 | ClientBin/
151 | [Ss]tyle[Cc]op.*
152 | ~$*
153 | *~
154 | *.dbmdl
155 | *.[Pp]ublish.xml
156 | *.pfx
157 | *.publishsettings
158 |
159 | # RIA/Silverlight projects
160 | Generated_Code/
161 |
162 | # Backup & report files from converting an old project file to a newer
163 | # Visual Studio version. Backup files are not needed, because we have git ;-)
164 | _UpgradeReport_Files/
165 | Backup*/
166 | UpgradeLog*.XML
167 | UpgradeLog*.htm
168 |
169 | # SQL Server files
170 | App_Data/*.mdf
171 | App_Data/*.ldf
172 |
173 | #############
174 | ## Windows detritus
175 | #############
176 |
177 | # Windows image file caches
178 | Thumbs.db
179 | ehthumbs.db
180 |
181 | # Folder config file
182 | Desktop.ini
183 |
184 | # Recycle Bin used on file shares
185 | $RECYCLE.BIN/
186 |
187 | # Mac crap
188 | .DS_Store
189 |
190 |
191 | #############
192 | ## Python
193 | #############
194 |
195 | *.py[cod]
196 |
197 | # Packages
198 | *.egg
199 | *.egg-info
200 | dist/
201 | build/
202 | eggs/
203 | parts/
204 | var/
205 | sdist/
206 | develop-eggs/
207 | .installed.cfg
208 |
209 | # Installer logs
210 | pip-log.txt
211 |
212 | # Unit test / coverage reports
213 | .coverage
214 | .tox
215 |
216 | #Translations
217 | *.mo
218 |
219 | #Mr Developer
220 | .mr.developer.cfg
221 | /packages/Mono.HttpUtility.1.0.0.1
222 |
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | MusicBee to Google Music Playlist Sync
2 | ======================================
3 |
4 | This is a [MusicBee](http://getmusicbee.com) plugin designed to allow users to synchronise their local playlists to Google Play and a .NET API for the Google Play Music website.
5 |
6 | Warning
7 | -------
8 |
9 | This plugin is in *ALPHA*. It has not been fully tested, and could do any number of things to your Google Music account. While I have used it to some extent on my own account without issues, there are many, many edge cases that I may have missed. Proceed with caution! *Don't use this if you value your Google Music playlists.* It may delete all of them.
10 |
11 | Plugin
12 | ------
13 |
14 | ### Installation
15 |
16 | To just use the plugin, download the built DLLs from the "Plugin" directory. Place both included DLLs into the MusicBee\\Plugins directory
17 |
18 | ### Usage
19 |
20 | In MusicBee, Preferences -> Plugins, enable and click "Configure".
21 |
22 | Enter your login details in the box, select to Remember Login and click "Login". The text below the email box will indicate if you've been successful.
23 |
24 | The plugin attempts to fetch your Google Music library and playlists after logging in. If you have a big library, this might take a while. Once fetched, Google Music playlists will be shown in the right hand box.
25 |
26 | To synchronise your MusicBee playlists to Google Music, select the playlists you'd like to synchronise from the left hand list, ensure the "To Google Music" radio button is selected and click "Synchronise Selected". To synchronise from Google Music to your computer, select the remote playlists you'd like to copy to MusicBee. Ensure that the "From Google Music" radio button is selected and click "Synchronise Selected" Check the "Sync on Startup" box to synchronise every time you start MusicBee. This will synchronise with whatever settings you set before you close the window, and is only one way (based on the radio button selection).
27 |
28 | ### How it Works
29 |
30 | On logging in the plugin fetches a full list of songs and playlists tied to your Google Music account. It goes through each local playlist you wish to synchronise and looks for a playlist with the same name on Google Music. If it finds one, it deletes it and then creates a new playlist with the local playlist's name. For each song in the local playlist, the plugin attempts to match it with a remote song. Songs it can find remote matches for are placed into the new playlist in order. It silently skips those it can't match.
31 |
32 | When synchronising Remote to local, it checks for a local playlist with the same name as the remote one. If it exists, then it deletes the .m3u file that playlist is contained in, and recreates it as a blank text file. If it doesn't exist, it creates a new playlist with that name. It then attempts to match each song in the remote playlist with a song in the local library. If it finds a match, it adds it to the new playlist, otherwise it skips that song silently.
33 |
34 | ### Limitations
35 |
36 | 1. Error handling is awful. If something goes wrong in communicating with Google, it probably won't tell you in a meaningful way.
37 |
38 | 2. It deletes the contents of existing playlists with the same name. Without another level of complexity it's not clear what the best solution to this is. The remote playlist appears to have a "lastModified" tag attached to it, so it may be possible to use that and local playlist monitoring to determine which is the newer playlist, but it would probably be quite a lot of work.
39 |
40 | 3. It's fairly buggy. My MusicBee plugin abilities are limited!
41 |
42 | C#/.NET API For Google Play Music
43 | ----------------------------------
44 |
45 | The plugin uses an incomplete implementation of an API for Google Play Music, [GooglePlayMusicApi](https://github.com/mitchhymel/GooglePlayMusicAPI), which is a port of the Python library, [gmusicapi](https://github.com/simon-weber/Unofficial-Google-Music-API)
46 |
47 |
--------------------------------------------------------------------------------
/MBGmusicSync.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 14
4 | VisualStudioVersion = 14.0.25420.1
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MBGMusic", "MBGmusic\MBGMusic.csproj", "{F5D46BA1-6F21-40EF-9695-46105CCACD08}"
7 | EndProject
8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestMBApi", "TestPlaylistCreation\TestMBApi.csproj", "{4D303419-70E9-4989-933D-9BDF8CBA87CA}"
9 | EndProject
10 | Project("{911E67C6-3D85-4FCE-B560-20A9C3E3FF48}") = "MusicBee", "..\..\..\Program Files (x86)\MusicBee\MusicBee.exe", "{DD3C2606-CAD7-4FD2-A53D-DD54F2200E1B}"
11 | ProjectSection(DebuggerProjectSystem) = preProject
12 | PortSupplier = 00000000-0000-0000-0000-000000000000
13 | Executable = C:\Program Files (x86)\MusicBee\MusicBee.exe
14 | RemoteMachine = MH-BOXBOX
15 | StartingDirectory = C:\Program Files (x86)\MusicBee
16 | Environment = Default
17 | LaunchingEngine = 00000000-0000-0000-0000-000000000000
18 | UseLegacyDebugEngines = No
19 | LaunchSQLEngine = No
20 | AttachLaunchAction = No
21 | EndProjectSection
22 | EndProject
23 | Global
24 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
25 | Debug|Any CPU = Debug|Any CPU
26 | Debug|Mixed Platforms = Debug|Mixed Platforms
27 | Debug|x86 = Debug|x86
28 | Release|Any CPU = Release|Any CPU
29 | Release|Mixed Platforms = Release|Mixed Platforms
30 | Release|x86 = Release|x86
31 | EndGlobalSection
32 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
33 | {F5D46BA1-6F21-40EF-9695-46105CCACD08}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
34 | {F5D46BA1-6F21-40EF-9695-46105CCACD08}.Debug|Any CPU.Build.0 = Debug|Any CPU
35 | {F5D46BA1-6F21-40EF-9695-46105CCACD08}.Debug|Mixed Platforms.ActiveCfg = Debug|x86
36 | {F5D46BA1-6F21-40EF-9695-46105CCACD08}.Debug|Mixed Platforms.Build.0 = Debug|x86
37 | {F5D46BA1-6F21-40EF-9695-46105CCACD08}.Debug|x86.ActiveCfg = Debug|x86
38 | {F5D46BA1-6F21-40EF-9695-46105CCACD08}.Debug|x86.Build.0 = Debug|x86
39 | {F5D46BA1-6F21-40EF-9695-46105CCACD08}.Release|Any CPU.ActiveCfg = Release|Any CPU
40 | {F5D46BA1-6F21-40EF-9695-46105CCACD08}.Release|Any CPU.Build.0 = Release|Any CPU
41 | {F5D46BA1-6F21-40EF-9695-46105CCACD08}.Release|Mixed Platforms.ActiveCfg = Release|x86
42 | {F5D46BA1-6F21-40EF-9695-46105CCACD08}.Release|Mixed Platforms.Build.0 = Release|x86
43 | {F5D46BA1-6F21-40EF-9695-46105CCACD08}.Release|x86.ActiveCfg = Release|x86
44 | {F5D46BA1-6F21-40EF-9695-46105CCACD08}.Release|x86.Build.0 = Release|x86
45 | {4D303419-70E9-4989-933D-9BDF8CBA87CA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
46 | {4D303419-70E9-4989-933D-9BDF8CBA87CA}.Debug|Any CPU.Build.0 = Debug|Any CPU
47 | {4D303419-70E9-4989-933D-9BDF8CBA87CA}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
48 | {4D303419-70E9-4989-933D-9BDF8CBA87CA}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
49 | {4D303419-70E9-4989-933D-9BDF8CBA87CA}.Debug|x86.ActiveCfg = Debug|Any CPU
50 | {4D303419-70E9-4989-933D-9BDF8CBA87CA}.Release|Any CPU.ActiveCfg = Release|Any CPU
51 | {4D303419-70E9-4989-933D-9BDF8CBA87CA}.Release|Any CPU.Build.0 = Release|Any CPU
52 | {4D303419-70E9-4989-933D-9BDF8CBA87CA}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
53 | {4D303419-70E9-4989-933D-9BDF8CBA87CA}.Release|Mixed Platforms.Build.0 = Release|Any CPU
54 | {4D303419-70E9-4989-933D-9BDF8CBA87CA}.Release|x86.ActiveCfg = Release|Any CPU
55 | {DD3C2606-CAD7-4FD2-A53D-DD54F2200E1B}.Debug|Any CPU.ActiveCfg = Release|x86
56 | {DD3C2606-CAD7-4FD2-A53D-DD54F2200E1B}.Debug|Mixed Platforms.ActiveCfg = Release|x86
57 | {DD3C2606-CAD7-4FD2-A53D-DD54F2200E1B}.Debug|x86.ActiveCfg = Release|x86
58 | {DD3C2606-CAD7-4FD2-A53D-DD54F2200E1B}.Release|Any CPU.ActiveCfg = Release|x86
59 | {DD3C2606-CAD7-4FD2-A53D-DD54F2200E1B}.Release|Mixed Platforms.ActiveCfg = Release|x86
60 | {DD3C2606-CAD7-4FD2-A53D-DD54F2200E1B}.Release|x86.ActiveCfg = Release|x86
61 | EndGlobalSection
62 | GlobalSection(SolutionProperties) = preSolution
63 | HideSolutionNode = FALSE
64 | EndGlobalSection
65 | EndGlobal
66 |
--------------------------------------------------------------------------------
/MBGmusic/Plugin/Settings.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.IO;
6 | using System.Xml.Serialization;
7 |
8 | namespace MusicBeePlugin
9 | {
10 | [Serializable]
11 | public class Settings
12 | {
13 | public Settings(string filename)
14 | {
15 |
16 | MBPlaylistsToSync = new List();
17 | GMusicPlaylistsToSync = new List();
18 |
19 | // defaults
20 | SyncOnStartup = false;
21 | SyncLocalToRemote = true;
22 |
23 | PlaylistDirectory = "GMusic";
24 |
25 | SettingsFile = filename;
26 |
27 | }
28 |
29 | public Settings()
30 | {
31 | MBPlaylistsToSync = new List();
32 | GMusicPlaylistsToSync = new List();
33 | }
34 |
35 | public String SettingsFile { get; set; }
36 |
37 | public Boolean SyncOnStartup { get; set; }
38 | public String AuthorizationToken { get; set; }
39 | public String Password { get; set; }
40 | public String Email { get; set; }
41 | public Boolean SaveCredentials { get; set; }
42 | public Boolean SyncLocalToRemote { get; set; }
43 | public List MBPlaylistsToSync { get; private set; }
44 | public List GMusicPlaylistsToSync { get; private set; }
45 | public String PlaylistDirectory { get; set; }
46 |
47 | public bool Save()
48 | {
49 | Settings.SaveSettings(this);
50 | return true;
51 | }
52 |
53 | public void Delete()
54 | {
55 | File.Delete(SettingsFile);
56 | /*
57 | try
58 | {
59 | // delete the config file
60 |
61 | return true;
62 | }
63 | catch
64 | {
65 | return false;
66 | }*/
67 | }
68 |
69 | public static Settings ReadSettings(string filename)
70 | {
71 | if (File.Exists(filename))
72 | {
73 | XmlSerializer controlsDefaultsSerializer = controlsDefaultsSerializer = new XmlSerializer(typeof(Settings));
74 |
75 | FileStream stream = File.Open(filename, FileMode.Open, FileAccess.Read, FileShare.None);
76 | StreamReader file = new StreamReader(stream, Encoding.UTF8);
77 | Settings settings = null;
78 | try
79 | {
80 | settings = (Settings)controlsDefaultsSerializer.Deserialize(file);
81 | }
82 | catch (Exception e)
83 | {
84 | Logger.Instance.Log("ERROR: Couldn't read saved settings");
85 | Logger.Instance.DebugLog("ERROR: Couldn't read saved settings");
86 | Logger.Instance.DebugLog(e.Message);
87 | return new Settings(filename);
88 | }
89 | finally
90 | {
91 | file.Close();
92 | }
93 | settings.SettingsFile = filename;
94 | return settings;
95 | }
96 | else
97 | {
98 | return new Settings(filename);
99 | }
100 | }
101 |
102 | public static void SaveSettings(Settings settings)
103 | {
104 | try
105 | {
106 | using (FileStream cfgFile = File.Open(settings.SettingsFile, FileMode.Create, FileAccess.Write, FileShare.None))
107 | {
108 | StreamWriter file = new StreamWriter(cfgFile, Encoding.UTF8);
109 | XmlSerializer controlsDefaultsSerializer = new XmlSerializer(typeof(Settings));
110 | controlsDefaultsSerializer.Serialize(file, settings);
111 | file.Close();
112 | }
113 | }
114 | catch
115 | {
116 | Logger.Instance.Log("ERROR: Couldn't save settings");
117 | Logger.Instance.DebugLog("ERROR: Couldn't save settings");
118 | }
119 |
120 | }
121 |
122 | }
123 | }
124 |
--------------------------------------------------------------------------------
/TestGMusicAPI/TestGMusicAPI.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Debug
5 | x86
6 | 8.0.30703
7 | 2.0
8 | {D61BD1FC-DE73-40B5-91DF-D8E756FC752C}
9 | WinExe
10 | Properties
11 | TestGMusicAPI
12 | TestGMusicAPI
13 | v4.5
14 |
15 |
16 | 512
17 |
18 |
19 | x86
20 | true
21 | full
22 | false
23 | bin\Debug\
24 | DEBUG;TRACE
25 | prompt
26 | 4
27 | false
28 |
29 |
30 | x86
31 | pdbonly
32 | true
33 | bin\Release\
34 | TRACE
35 | prompt
36 | 4
37 | false
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 | Form
54 |
55 |
56 | Form1.cs
57 |
58 |
59 |
60 |
61 |
62 | Form1.cs
63 |
64 |
65 | ResXFileCodeGenerator
66 | Resources.Designer.cs
67 | Designer
68 |
69 |
70 | True
71 | Resources.resx
72 | True
73 |
74 |
75 |
76 | SettingsSingleFileGenerator
77 | Settings.Designer.cs
78 |
79 |
80 | True
81 | Settings.settings
82 | True
83 |
84 |
85 |
86 |
87 | {F5D46BA1-6F21-40EF-9695-46105CCACD08}
88 | MBGMusic
89 |
90 |
91 |
92 |
99 |
--------------------------------------------------------------------------------
/TestPlaylistCreation/TestWindow.Designer.cs:
--------------------------------------------------------------------------------
1 | namespace TestPlaylistCreation
2 | {
3 | partial class TestWindow
4 | {
5 | ///
6 | /// Required designer variable.
7 | ///
8 | private System.ComponentModel.IContainer components = null;
9 |
10 | ///
11 | /// Clean up any resources being used.
12 | ///
13 | /// true if managed resources should be disposed; otherwise, false.
14 | protected override void Dispose(bool disposing)
15 | {
16 | if (disposing && (components != null))
17 | {
18 | components.Dispose();
19 | }
20 | base.Dispose(disposing);
21 | }
22 |
23 | #region Windows Form Designer generated code
24 |
25 | ///
26 | /// Required method for Designer support - do not modify
27 | /// the contents of this method with the code editor.
28 | ///
29 | private void InitializeComponent()
30 | {
31 | this.CreatePlaylist_button = new System.Windows.Forms.Button();
32 | this.CreatePlaylistDirectory_tb = new System.Windows.Forms.TextBox();
33 | this.label1 = new System.Windows.Forms.Label();
34 | this.label2 = new System.Windows.Forms.Label();
35 | this.CreatePlaylistName_tb = new System.Windows.Forms.TextBox();
36 | this.SuspendLayout();
37 | //
38 | // CreatePlaylist_button
39 | //
40 | this.CreatePlaylist_button.Location = new System.Drawing.Point(255, 64);
41 | this.CreatePlaylist_button.Name = "CreatePlaylist_button";
42 | this.CreatePlaylist_button.Size = new System.Drawing.Size(139, 23);
43 | this.CreatePlaylist_button.TabIndex = 0;
44 | this.CreatePlaylist_button.Text = "Create Playlist";
45 | this.CreatePlaylist_button.UseVisualStyleBackColor = true;
46 | this.CreatePlaylist_button.Click += new System.EventHandler(this.CreatePlaylist_button_Click);
47 | //
48 | // CreatePlaylistDirectory_tb
49 | //
50 | this.CreatePlaylistDirectory_tb.Location = new System.Drawing.Point(108, 12);
51 | this.CreatePlaylistDirectory_tb.Name = "CreatePlaylistDirectory_tb";
52 | this.CreatePlaylistDirectory_tb.Size = new System.Drawing.Size(286, 20);
53 | this.CreatePlaylistDirectory_tb.TabIndex = 1;
54 | //
55 | // label1
56 | //
57 | this.label1.AutoSize = true;
58 | this.label1.Location = new System.Drawing.Point(53, 15);
59 | this.label1.Name = "label1";
60 | this.label1.Size = new System.Drawing.Size(49, 13);
61 | this.label1.TabIndex = 2;
62 | this.label1.Text = "Directory";
63 | //
64 | // label2
65 | //
66 | this.label2.AutoSize = true;
67 | this.label2.Location = new System.Drawing.Point(67, 41);
68 | this.label2.Name = "label2";
69 | this.label2.Size = new System.Drawing.Size(35, 13);
70 | this.label2.TabIndex = 4;
71 | this.label2.Text = "Name";
72 | //
73 | // CreatePlaylistName_tb
74 | //
75 | this.CreatePlaylistName_tb.Location = new System.Drawing.Point(108, 38);
76 | this.CreatePlaylistName_tb.Name = "CreatePlaylistName_tb";
77 | this.CreatePlaylistName_tb.Size = new System.Drawing.Size(286, 20);
78 | this.CreatePlaylistName_tb.TabIndex = 3;
79 | //
80 | // TestWindow
81 | //
82 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
83 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
84 | this.ClientSize = new System.Drawing.Size(406, 315);
85 | this.Controls.Add(this.label2);
86 | this.Controls.Add(this.CreatePlaylistName_tb);
87 | this.Controls.Add(this.label1);
88 | this.Controls.Add(this.CreatePlaylistDirectory_tb);
89 | this.Controls.Add(this.CreatePlaylist_button);
90 | this.Name = "TestWindow";
91 | this.Text = "TestWindow";
92 | this.ResumeLayout(false);
93 | this.PerformLayout();
94 |
95 | }
96 |
97 | #endregion
98 |
99 | private System.Windows.Forms.Button CreatePlaylist_button;
100 | private System.Windows.Forms.TextBox CreatePlaylistDirectory_tb;
101 | private System.Windows.Forms.Label label1;
102 | private System.Windows.Forms.Label label2;
103 | private System.Windows.Forms.TextBox CreatePlaylistName_tb;
104 | }
105 | }
--------------------------------------------------------------------------------
/TestGMusicAPI/Form1.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.ComponentModel;
4 | using System.Data;
5 | using System.Drawing;
6 | using System.Linq;
7 | using System.Text;
8 | using System.Windows.Forms;
9 | using MusicBeePlugin;
10 | using MusicBeePlugin.GMusicAPI;
11 | using MusicBeePlugin.Models;
12 |
13 | namespace TestGMusicAPI
14 | {
15 | public partial class Form1 : Form
16 | {
17 | private List AllPlaylists = new List();
18 | private List AllSongs = new List();
19 | private List AllEntries = new List();
20 |
21 | API api = new API();
22 | APIOAuth apiOauth = new APIOAuth();
23 |
24 | public Form1()
25 | {
26 | InitializeComponent();
27 | }
28 |
29 | private async void loginButton_Click(object sender, EventArgs e)
30 | {
31 | bool loggedIn = await apiOauth.LoginAsync(Secret.USERNAME, Secret.PASSWORD);
32 | this.statusLabel.Text = String.Format("Login Status: {0}", loggedIn);
33 | }
34 |
35 | private async void getTracksButton_Click(object sender, EventArgs e)
36 | {
37 | List library = await apiOauth.GetLibraryAsync();
38 | AllSongs = library;
39 | foreach(GMusicSong song in library)
40 | {
41 | songListBox.Items.Add(song);
42 | }
43 |
44 | songTotalLabel.Text = String.Format("Total songs: {0}", library.Count);
45 | }
46 |
47 | #region Get playlists
48 | // Get all the playlists tied to this account and display them
49 |
50 | private async void getPlaylistsButton_Click(object sender, EventArgs e)
51 | {
52 | List playlists = await apiOauth.GetPlaylistsWithEntriesAsync();
53 | AllPlaylists = playlists;
54 | playlistListBox.Items.Clear();
55 | foreach (GMusicPlaylist playlist in playlists)
56 | {
57 | playlistListBox.Items.Add(playlist);
58 | }
59 | }
60 |
61 | #endregion
62 |
63 | #region Create playlists
64 | private async void createPlaylistButton_Click(object sender, EventArgs e)
65 | {
66 | await apiOauth.CreatePlaylistAsync(createPlaylistName.Text);
67 | getPlaylistsButton.PerformClick();
68 | }
69 |
70 | #endregion
71 |
72 | #region Delete playlist
73 | // Deletes the playlist currently selected by the user
74 | private async void deletePlaylistButton_Click(object sender, EventArgs e)
75 | {
76 | GMusicPlaylist selectedPlaylist = (GMusicPlaylist)playlistListBox.SelectedItem;
77 | await apiOauth.DeletePlaylistAsync(selectedPlaylist.ID);
78 | getPlaylistsButton.PerformClick();
79 | }
80 |
81 | #endregion
82 |
83 | #region Modify playlists
84 | private async void addSongsButton_Click(object sender, EventArgs e)
85 | {
86 | ListBox.SelectedObjectCollection songsSelected = songListBox.SelectedItems;
87 | List songsList = new List();
88 | foreach (GMusicSong song in songsSelected)
89 | songsList.Add(song);
90 |
91 | GMusicPlaylist selectedPlaylist = (GMusicPlaylist)playlistListBox.SelectedItem;
92 |
93 | await apiOauth.AddToPlaylistAsync(selectedPlaylist.ID, songsList);
94 | getPlaylistsButton.PerformClick();
95 |
96 | }
97 |
98 | // This could delete multiple selections at once, but it doesn't
99 | private async void deleteSong_Click(object sender, EventArgs e)
100 | {
101 | GMusicSong selectedSong = (GMusicSong)playlistSongsBox.SelectedItem;
102 | GMusicPlaylist selectedPlaylist = (GMusicPlaylist)playlistListBox.SelectedItem;
103 | GMusicPlaylistEntry songEntry = selectedPlaylist.Songs.First(s => s.TrackID == selectedSong.ID);
104 | await apiOauth.RemoveFromPlaylistAsync(new List { songEntry });
105 | }
106 |
107 | #endregion
108 |
109 | #region Update playlist songs
110 | private void playlistListBox_SelectedIndexChanged(object sender, EventArgs e)
111 | {
112 | playlistSongsBox.Items.Clear();
113 | GMusicPlaylist selectedPlaylist = (GMusicPlaylist)playlistListBox.SelectedItem;
114 | foreach (GMusicPlaylistEntry song in selectedPlaylist.Songs)
115 | {
116 | if (!song.Deleted)
117 | {
118 | GMusicSong thisSong = AllSongs.FirstOrDefault(s => s.ID == song.TrackID);
119 | if (thisSong != null)
120 | {
121 | playlistSongsBox.Items.Add(thisSong);
122 | }
123 | }
124 | }
125 | }
126 |
127 | #endregion
128 |
129 | private async void renameButton_Click(object sender, EventArgs e)
130 | {
131 | GMusicPlaylist selectedPlaylist = (GMusicPlaylist)playlistListBox.SelectedItem;
132 | await apiOauth.UpdatePlaylistAsync(selectedPlaylist.ID, renamePlaylistTextBox.Text, description:"test test new description test");
133 | getPlaylistsButton.PerformClick();
134 | }
135 |
136 |
137 | }
138 | }
139 |
--------------------------------------------------------------------------------
/TestGMusicAPI/Properties/Resources.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 | text/microsoft-resx
107 |
108 |
109 | 2.0
110 |
111 |
112 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
113 |
114 |
115 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
--------------------------------------------------------------------------------
/MBGmusic/Configure.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 | text/microsoft-resx
110 |
111 |
112 | 2.0
113 |
114 |
115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
119 |
120 |
--------------------------------------------------------------------------------
/TestPlaylistCreation/TestWindow.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 | text/microsoft-resx
110 |
111 |
112 | 2.0
113 |
114 |
115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
119 |
120 |
--------------------------------------------------------------------------------
/TestPlaylistCreation/MBTestAPIPlugin.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.InteropServices;
3 | using System.Drawing;
4 | using System.Windows.Forms;
5 | using System.Collections.Generic;
6 | using System.Linq;
7 | using System.Threading;
8 | using System.IO;
9 | using System.Text;
10 | using TestPlaylistCreation;
11 |
12 | namespace MusicBeePlugin
13 | {
14 | public partial class Plugin
15 | {
16 |
17 | public MusicBeeApiInterface mbApiInterface;
18 |
19 | private PluginInfo about = new PluginInfo();
20 |
21 | #region Plugin Exported Methods
22 |
23 | public PluginInfo Initialise(IntPtr apiInterfacePtr)
24 | {
25 | mbApiInterface = new MusicBeeApiInterface();
26 | mbApiInterface.Initialise(apiInterfacePtr);
27 | about.PluginInfoVersion = PluginInfoVersion;
28 | about.Name = "MB Test API Plugin";
29 | about.Description = "Quickly test API functionality";
30 | about.Author = "Leo Rampen";
31 | about.TargetApplication = "None"; // current only applies to artwork, lyrics or instant messenger name that appears in the provider drop down selector or target Instant Messenger
32 | about.Type = PluginType.General;
33 | about.VersionMajor = 1; // your plugin version
34 | about.VersionMinor = 1;
35 | about.Revision = 1;
36 | about.MinInterfaceVersion = MinInterfaceVersion;
37 | about.MinApiRevision = MinApiRevision;
38 | about.ReceiveNotifications = (ReceiveNotificationFlags.PlayerEvents | ReceiveNotificationFlags.TagEvents);
39 | about.ConfigurationPanelHeight = 0; // not implemented yet: height in pixels that musicbee should reserve in a panel for config settings. When set, a handle to an empty panel will be passed to the Configure function
40 |
41 |
42 | // Comment this out to disable logging
43 | // Logger.Instance.LogFile = System.IO.Path.Combine(mbApiInterface.Setting_GetPersistentStoragePath(), "gMusicPlaylistSync.log.txt");
44 |
45 | createMenu();
46 |
47 | return about;
48 | }
49 |
50 | private TestWindow _testWindow;
51 |
52 | public bool Configure(IntPtr panelHandle)
53 | {
54 |
55 | int backColor = mbApiInterface.Setting_GetSkinElementColour(SkinElement.SkinInputControl, ElementState.ElementStateDefault,
56 | ElementComponent.ComponentBackground);
57 | int foreColor = mbApiInterface.Setting_GetSkinElementColour(SkinElement.SkinInputControl, ElementState.ElementStateDefault,
58 | ElementComponent.ComponentForeground);
59 |
60 | return false;
61 | }
62 |
63 | private void createMenu()
64 | {
65 | mbApiInterface.MB_AddMenuItem("mnuTools/Test MB API Plugin", "", onMenuItemClick);
66 | }
67 |
68 | private void onMenuItemClick(object sender, EventArgs e)
69 | {
70 | if (_testWindow != null)
71 | {
72 | _testWindow.Dispose();
73 | _testWindow = null;
74 | }
75 |
76 | _testWindow = new TestWindow(mbApiInterface);
77 |
78 | _testWindow.Show();
79 | }
80 |
81 | // called by MusicBee when the user clicks Apply or Save in the MusicBee Preferences screen.
82 | // its up to you to figure out whether anything has changed and needs updating
83 | public void SaveSettings()
84 | {
85 | }
86 |
87 | // MusicBee is closing the plugin (plugin is being disabled by user or MusicBee is shutting down)
88 | public void Close(PluginCloseReason reason)
89 | {
90 | }
91 |
92 |
93 |
94 | // uninstall this plugin - clean up any persisted files
95 | public void Uninstall()
96 | {
97 | }
98 |
99 |
100 | // receive event notifications from MusicBee
101 | // you need to set about.ReceiveNotificationFlags = PlayerEvents to receive all notifications, and not just the startup event
102 | public void ReceiveNotification(string sourceFileUrl, NotificationType type)
103 | {
104 | // perform some action depending on the notification type
105 | switch (type)
106 | {
107 | case NotificationType.PluginStartup:
108 | // Do a sync straight after we get the playlists
109 | /*
110 | if (SavedSettings.syncOnStartup)
111 | {
112 | OnFetchDataComplete = SyncPlaylists;
113 | LoginToGMusic();
114 | }*/
115 |
116 | break;
117 | }
118 | }
119 |
120 | // return an array of lyric or artwork provider names this plugin supports
121 | // the providers will be iterated through one by one and passed to the RetrieveLyrics/ RetrieveArtwork function in order set by the user in the MusicBee Tags(2) preferences screen until a match is found
122 | public string[] GetProviders()
123 | {
124 | return null;
125 | }
126 |
127 | // return lyrics for the requested artist/title from the requested provider
128 | // only required if PluginType = LyricsRetrieval
129 | // return null if no lyrics are found
130 | public string RetrieveLyrics(string sourceFileUrl, string artist, string trackTitle, string album, bool synchronisedPreferred, string provider)
131 | {
132 | return null;
133 | }
134 |
135 | // return Base64 string representation of the artwork binary data from the requested provider
136 | // only required if PluginType = ArtworkRetrieval
137 | // return null if no artwork is found
138 | public string RetrieveArtwork(string sourceFileUrl, string albumArtist, string album, string provider)
139 | {
140 | //Return Convert.ToBase64String(artworkBinaryData)
141 | return null;
142 | }
143 |
144 | #endregion
145 |
146 | }
147 | }
--------------------------------------------------------------------------------
/MBGmusic/Plugin/GMusicSyncData.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading;
6 | using MusicBeePlugin.Models;
7 | using System.Threading.Tasks;
8 | using GooglePlayMusicAPI;
9 |
10 | namespace MusicBeePlugin
11 | {
12 | class GMusicSyncData
13 | {
14 | private Settings _settings;
15 |
16 | private Logger log;
17 |
18 | private Plugin.MusicBeeApiInterface _mbApiInterface;
19 |
20 | public GMusicSyncData(Settings settings, Plugin.MusicBeeApiInterface mbApiInterface)
21 | {
22 | _allPlaylists = new List();
23 | _allSongs = new List