Need a way to retrieve the current playing song from Zune and Windows Media Player with Python
Asked Answered
C

2

9

An Application of mine retrieves the current playing song from a multitude of music players. However, I'm having great trouble implementing Zune and Windows Media Player.

I've done a lot of googling on the subject, unfortunately it's only confusing me more and more.

What I would normally do for my other applications:

  1. Iterate over all open windows every 4 seconds
  2. Get the title of all windows
  3. Check title for a pattern (Ie, " - Spotify ")
  4. If it's there, adjust the title for output.

WMP Does not have the current playing song in the title.

Zune does, but it's rotating every few seconds between title, album and artist. Which is heavily unreliable to track with my current method, albeit possible.

Windows Media Player

I've also tried using the COM component for windows media player.

import win32com.client
wmp = win32com.client.gencache.EnsureDispatch('WMPlayer.OCX')

# some function I don't have here, it retrieves the current playing song
# and other data

The big problem with that it requires you to start WMP programmatically, which would be extremely user unfriendly

So, what have I found? This SO post redirects to WMP.dll. But as far as I've read, it has the same problem as the COM, you have to start it programmatically. If not, I would really like some directions on how to work with that dll in python.

There would be another a little less hacky solution, which is to write a plugin for WMP, let my users download that plugin and retrieve the data from that plugin. I'd rather not go there, since I have no experience with any of the C languages, nor do I feel like digging into plugin documentations for this.

Zune

A method would be to cycle through the three title states, determine which state it's currently at and find the position of the other two.

IE: First 5 seconds the title is: Super_song Next 5 seconds the title is: By Power_artist Next 5 seconds the title is: Good_album (date)

So I could determine when the album title is by making a regex for the date (which is always there) and then find the title and artist by waiting a few seconds.

This is obviously not a great solution, since it'll take a while and it's not very reliable either, (what if the song name contains a date for example)

The next problem is that it's not consistent either, sometimes the title just stays Zune for minutes long. No idea why.

So, move on to the next method.

There's this application called ZuneNowPlaying. This "somehow" gets the current playing song from Zune and puts it in the registry, this thing does not work with my sloppy title method, since it changes the registry the instant the song changes. Immediately.

This is the solution I had used in the working version of my program, but many users reported that it simply didn't work, nothing happened. And I checked the program and it doesn't reliably change the registry all the time. I don't know why, I don't know how to fix it. Therefor, this solution is also -scrapped-.

Is the fact that it is using the name "MsnMsgrUIManager"#000000"> causing the zune software to send it information about which song is playing? Is there a way to get this information without this kind of hack?

That is found in the discussion of the Zune Now Playing application. The source is not available unfortunately, at least I can't find it. Anyone got more on this?

Third method I had heard of was once again, a dll. ZuneShell.dll it's called. I don't remember where I read about it, nor can I find it via google, since all results are "Is ZuneShell.dll a virus?".

Once again, I run into the problem that I wouldn't know how to work with this even IF I had documentation on it, heck, if it's even what I have been looking for.

Alternate directions to maybe look into

While browsing about this subject, I've seen people talking about retrieving data directly from GUI's. I'm not sure how legit, possible or even how correct my memory of it is, but if it's possible could someone redirect me to more on this?

Anything else, really.

Clanton answered 18/10, 2013 at 14:20 Comment(4)
Have you considered trying to read the data from the memory of the zune process? There seems to be a (outdated) library to do that in python, named pymemHorsehair
@goncalopp I have not, and I shall look into it.Clanton
It seems possible to gain access to a running instance of WMP using the dll, but it's not so easy me thinks, see here. Maybe you could get some other inspiration from the source of the "CurrentTrack" plugin for Pidgin. Fair warning: it's in C++ and has the Author comment: "this solution sucks... but until I can figure out how to use the COM API..."Mendie
Sources of ZuneNowPlaying are available znp.codeplex.com/SourceControl/latest#ZuneNowPlaying/…Pavid
P
3

I have working code in C++ to print the name of media currently playing in WMP. It's a simple console application (78 lines of code).

Steps:

1) implements a basic COM object implementing IUnknown, IOleClientSite, IServiceProvider and IWMPRemoteMediaServices. This is straightforward (sort of, your mileage may vary) using the ATL template CComObjectRootEx. The only methods needing (simple) code are IServiceProvider::QueryService and IWMPRemoteMediaServices::GetServiceType. All other methods may return E_NOTIMPL

2) Instantiate the "WMPlayer.OCX" COM object (in my case, via CoCreateInstance)

3) Retrieve from the object an IOleObject interface pointer via QueryInterface

4) Instanciate an object from the class seen in 1) (I use the CComObject<>::CreateInstance template)

5) Use the SetClientSite method from the interface you got at 3), passing a pointer to your OleClientSite implementation.

6) During the SetClientSite call, WMP will callback you: fisrt asking for an IServiceProvider interface pointer, second calling the QueryService method, asking for an IWMPRemoteMediaServices interface pointer. Return your implementation of IWMPRemoteMediaServices and, third, you will be called again via GetServiceType. You must then return "Remote". You are now connected to the WMP running instance

7) Query the COM object for an IWMPMedia interface pointer

8) If 7) didn't gave NULL, read the the IWMPMedia::name property.

9) DONE

All the above was tested with VS2010 / Windows Seven, and with WMP running (if there is no Media Player process running, just do nothing).

I don't know if yoy can/want to implement COM interface and object in Python. If you are interested by my C++ code, let me know. You could use that code in a C++ DLL, and then call it from python.

Pavid answered 24/10, 2013 at 16:18 Comment(8)
This explanation is unfortunately too vague for me, I have absolutely no idea what to do at the first step already. And there's so little documentation on using the COM with scripting languages (in particular python of course). Edit: If you can't help me out with the Python, I'll open up a new question to redirect the question to python win32api users.Clanton
@Clanton I can't help with Python. You are left with 2 alternatives: 1) build a DLL (or an EXE) in C++, which will be a COM Server, callable from Python. Or 2) Implement the whole thing directly in Python, IF it is possible to implement in Python an interface which do not inherit from IDispatch.Pavid
I know this has been a while, however I still have this problem. I know a lot more about C++, python and everything now. If you still have the c++ code you mentioned in this answer, could you send it to my email? [email protected]Clanton
Holiday time, here, I will do some archeological works next week. Poke me if I seem to have forgotten.Pavid
Hey, here's your poke :)Clanton
Found on my old PC. Still Works. Build env is Visual Studio 2010. Do you want all the Solution files, or just the unique c++ file?Pavid
If possible, I'd like the whole package. Thanks for digging it up!Clanton
Will send email tomorrow morning. France, here.Pavid
F
1

I just found a cool Python tool which can query all the controls of any program. Simple, straightforward, and easy to read. It's here:

http://www.brunningonline.net/simon/blog/archives/winGuiAuto.py.html

With that you can get the info from the GUI.

You can also grab the loaded file list. It works for most media player. You can get this information programmatically like this:

http://www.codeproject.com/Articles/18975/Listing-Used-Files

This is C++, but at that point you can wrap the native code. This way you have to extract the ID3 tags yourself. Might worth the shot as it would be an universal solution.

Fawcette answered 24/10, 2013 at 9:43 Comment(2)
Retrieving all the files actually sounds like a decent back-up plan. However, WMP has two files open a lot of the time, so that's hard to work with. I think I'll look more into this if all else fails.Clanton
It doesn't query all the files, only the one which is playing. Every song is displayed in the playlist of WMP, but only the currently played song is opened for reading continuously. My second suggested solution looks for just that one opened file handle. By the way, what do you think about pywinauto?Fawcette

© 2022 - 2024 — McMap. All rights reserved.