Scripting Tips & Tricks

From MediaMonkey Wiki
Jump to navigation Jump to search

(MediaMonkey) Scripting in general

'Correctly' update song properties

Using the SongData object, you can either update the database/library (UpdateDB) and/or update the file's tags (WriteTags). So there's no way to let the song changes be saved like the user selected in the Options panel (menu Tools > Options > Library > Tags & Playlists > "Update tags when editing properties"). However, the UpdateAll method for the SongList object does take into account this user-specified option. So you can create a new SongList object using SDB.NewSongList and add your SDBSongData object(s) to it. Then call the UpdateAll method on the songlist, and your song(s) will be saved in user-specified way.

See MM debug log via DebugView

You can download DebugView directly from Microsoft. If used with the debug version of MM, lots of useful messages will be displayed in DebugView. Also, any message set via ISDBTools::OutputDebugStringMM (e.g. using SDB.Tools.OutputDebugString("Test1")) will also be added to that debug log.

See MM Scripting interface using OLEView

MS Visual Studio contains a tool called OLEView ("OLE Viewer" or "OLE/COM Object Viewer") (direct link), which can show the type library of the SongsDB object "MediaMonkey Library". It requires the Iviewers.dll, which is not included, but can be downloaded from here or other 3rd-party locations. There, you can see all supported properties and methods of the MediaMonkey scripting interface. This can be handy to find out new scripting methods in case that the scripting reference isn't up-to-date yet.
The way to get there: OleView > Type Libraries > MediaMonkey Library > (double click)
and then in the ITypeLib Viewer > SongsDB (MediaMonkey Library) > Interfaces.

Detecting docking of a panel

When you un(dock) a dockable panel, the OnResize event is triggered twice:

  • Once while the panel is being (un)docked with DockedTo = 0
  • Once after the panel has been (undocked): with DockedTo = -1 (panel undocked) or DockedTo = X (panel docked, X is the number that represents the docking position)

Keep reference to Form/Panel

When you create a form or panel, always add a reference to the SDB.Objects dictionary. If you don't add it, the form/panel will be gone immediately after you created it, with or without error message. Instead of using the SDB.Objects dictionary you can also store a reference as a global script variable, in most cases.

Event mechanisms

There are still 2 event mechanisms available:

  • Using the (mostly deprecated) old event mechanism (using Control.OnClickFunc and Control.UseScript), your global variables aren't remembered in the event handlers (e.g. kind of like the script is reopened). For those scripts, you had to use ParentControl.ChildControl("control name") or save object references to SDB's Objects dictionary.
  • Using the new event mechanism, your global variables are remembered so you can use their values in the event handlers. The Objects dictionary is now only necessary to communicate/store object references between different scripts.

So always use the new event mechanism where you can. Only for option panels (sheets) this is not yet possible. There you'll need to store values between the main program and the event handlers in SDB.Objects, in the INI or in the Registry.

Internal mechanism of script loading

Normally, you don't need to know much about internal handling of scripts by MediaMonkey, but sometimes it might be useful to know, when exactly scripts are loaded.

When some function of a script is about to be executed (e.g. OnStartup function of auto-scripts), the script is loaded to memory and executed as necessary. When the execution finishes, the script is again unloaded from memory. However, there is on exception - if you register some event (by calling RegisterEvent function), the script remains still loaded in memory, until all registered events are unregistered, then it's unloaded.

This is good to know when you plan some sharing of variables. While the script is loaded in memory, you can use its global variables, but if you need to persist some values or objects between script sessions, you have to use some other mechanism (ini files, Objects collection, ...).

Note that starting from MediaMonkey, there is always at most one instance of script loaded in memory. In older versions, if a script registered some events and then e.g. Option sheet functions were called back by MediaMonkey, they were processed in another instance of the script. Now they are processed in the already loaded instance, which helps avoid some memory leaks, is easier to handle and is also faster to process.

Changing the current tree node

Changing the selected node from a script (using SDB.MainTree.CurrentNode = ...........) takes some time to happen, and it happens asynchronicly (the code execution doesn't wait until the node is really selected). For that reason, you can't use (get value of) SDB.MainTree.CurrentNode directly after you have assigned a new node to it (set value to), because changes won't have happened yet.

Interaction with MediaMonkey from outside

There are at least three ways to interact with MediaMonkey from an external program:

  • Open the MediaMonkey.exe program with command line options (arguments).
  • Using the same Windows Messages (SendMessage/PostMessage) as in Winamp (WM_USER and WM_COMMAND). MM partially emulates how WinAmp works (not 100%, but very close) and so it can use WinAmp plug-ins. The class name to communicate with is "Winamp v1.x".
  • Using OLE Automation, which can be very easily used e.g. from VB Script.
    Dim SDB : Set SDB = CreateObject("SongsDB.SDBApplication")
    SongsDB.SDBApplicationClass SDB = new SongsDB.SDBApplicationClass();
    Be sure to set SDB.ShutdownAfterDisconnect to False if you opened the MediaMonkey program by calling the SDB object, and you don't want it to be closed when your external program exits (or if you disconnect the SDB object = COM link).

Making scripts ready for per-user installations

Starting from MediaMonkey 4.0, scripts can be (and are suggested to be) installed per-user, and so they don't have to be installed in Program Files folder, but are installed in local Windows folders. The mechanism in MM is internally made as transparent as possible, so that scripts don't have to care much about their installed location. That said, some pre-4.0 scripts might need tweaking. For example, a popular script Lyricator has >1000 lines, but there's only one change needed in order to get it working under local installation, namely:

Dim sAppPath: sAppPath = sdb.ApplicationPath & "Scripts\Lyricator\"

needs to be changed to

Dim sAppPath: sAppPath = sdb.ScriptsPath & "Lyricator\"

Visual Basic Script

Index in VBScript

Usually indexes start from 0 (e.g. for normal arrays). However, sometimes (e.g. for string positions or for ISDBCommonDialog::FilterIndex) they start from 1.

Code readability

To maintain good readability of your scripting code, make sure to add comments where necessary, split large methods in smaller ones, use appropriate indentation (tabs or 2/3/4 spaces), and if necessary split long lines of code over several lines. To split a line of VBScript code (not text or numbers), add a space and an underscore, and continue on the next line. Don't forget the space before the underscore or it won't work.

MsgBox "This is quite a long text to display on one single line and for readability we better split it on multiple lines"

MsgBox "This is quite a long text to display on one single line" & " and for readability we better split it on multiple lines"

MsgBox "This is quite a long text to display on one single line" _
     & " and for readability we better split it on multiple lines"

In the third example the code is more readable, but the result is still the same: a message box with one long line of text. To split this over multiple lines, we preferably use the constant vbNewLine. If necessary, you can also use vbCr (Unix-style) or vbCrLf(Windows-style). Using these constants is more clear than using respectively Chr(13) (Unix-style) or Chr(13) & Chr(10) (Windows-style).

MsgBox "This is quite a long text to display on one single line" & vbNewLine _
     & " and for readability we better split it on multiple lines"

Using Flags in VBScript

The concept of Flags can be a difficult for novice programmers. Click the link for an explanation of how to use them in VBScript (or Visual Basic).


Helpful sites:

VBScript editors:


Passing arrays to MediaMonkey methods

To the MediaMonkey COM methods accepting array as their argument you cannot simply pass native JScript array. You have to convert it to VBScript-compatible array (specifically VBSafeArray) first. (This applies yet in MM 4.1.) One example is the ISDBApplication::MessageBox method which accepts button constants as array in an argument.

You can use function from snippet below to JScript native array to the VBScript-compatible array.

 * Return VBSafeArray containing items from JScript array.
function array_to_vbarray(array) {
	var dict = new ActiveXObject('Scripting.Dictionary');
	for (var i = 0, len = array.length; i < len; i++) {
		dict.add(i, array[i]);
	return dict.Items();

Then, when calling MediaMonkey methods, simply wrap the array argument with array_to_vbarray(…) call:

// Display message box
SDB.MessageBox("Simple message box from JScript with mtWarning icon and one mbOk button.", 0, array_to_vbarray([4]));

Adding object to SDB.Objects

Object stored in SDB.Objects can be retrieved by simple method call: SDB.Objects('MyObject'). But how to set the object at first?

JScript has special notation for this: SDB.Objects('MyObject') = my_object. This is not syntax error. You simply assign value to the reference returned by the method call. It works same way as in VBScript.

Working with dates

There are MediaMonkey COM properties of type Date (such as Date, DateAdded, LastPlayed in SDBSongData). When used in JScript, they require special treatment. (This applies yet in MM 4.1.)

Reading: Properties are not converted to native JScript Date instance. You have to do it manually – cast them to string and then manually create the Date instance.

Setting: You cannot set native JScript Date instance to the property. But you can set there a string in the ISO format YYYY-MM-DD HH:II:SS (eg. 2012-12-31 01:02:03).

Database & SQL

Database transactions

Starting from MM 3.0 transactions are often used when working with database. It can cause some problems to script authors in case they don't know some details. One example where you can face a problem is when you use ISDBSongData::UpdateDB method while you have some SQL query open. What happens in this case is, that MM wants to start a transaction, but there's still an SQL query open. This results either in an error message (in debug build) or possibly an apparent freeze of MM in the release build.

In order to prevent this problem, you can either:

Creating complex SQL queries

To easily create complex SQL queries, you can use the Query graphical user interface in Access interface, and show the SQL code when you are done. In any case, make sure that your SQL code is organized is such a way that it can be understood easily, e.g. by putting SELECT, INSERT, FROM, ORDER BY, ... on separate lines with appropriate indentation.

Avoid SQL date problems

One of the problems with SQL is that dates are often confused, which sometimes give programming bugs. The best way to circumvent this problem is by using the format #yyyy-mm-dd# for all dates in SQL (e.g. #2006-10-31# for 31th October 2006).


Helpful sites: