Database updating?

I’ve tried several combinations of code to attempt to update an existing item in mAirListDB, but nothing seems to work; and it seems to ‘semi-freeze’ mAirList when I try to run a ‘faulty’ script.

What would be the correct sequence of statements to be able to typecast, connect to, update an exsiting item in, and eventually disconnect from a mAirListDB? In my case, there is only the one Database defined (is it possible to have more than one defined in Config at the same time?), but obviously there may be more than one type in the list (e.g. a mAirListDB, and iTunes, and an OTF ‘database’).

At some point in this sequence, I am aiming to be able to execute a statement like:
in a loop, so I can process an entire Playlist.
In that example, mairlistDB would be an IDatabase var, and currentItem would be an IPlaylistItem var.

All help appreciated!


item.GetDatabase will return the “unique ID” of the database the item was loaded from, or an empty string if its not from a database at all.

You pass this value to Instance.GetDatabases.FindByUniqueID, which will return an IDatabase reference to the database in question, or nil if it is unknown.

If the return value (stored in a variable named say “db”) is not nil, you can use db.SavePlaylistItem to save the item.

Databases are connected permanently, you do not need to connect it first.

OK, so what I need to try is this:

var currentItem: IPlaylistItem; dbMairlist: IDatabase; sDatabaseName: string; begin currentItem := CurrentPlaylist.GetItem(0); sDatabaseName := currentItem.GetDatabase; if sDatabaseName <> '' then begin dbMairlist := Instance.GetDatabases.FindByUniqueID(sDatabaseName); if dbMairlist <> nil then try dbMairlist.SavePlaylistItem(currentItem); except SystemLog('Could not save item to database' + sDatabaseName + '.'); end; end.

Thanks for that, Torben. :slight_smile: I’ll give that a try and assuming I have everything working correctly, I should soon have a script which will ‘add’ an item Type to existing MMDs, mAirList database entries, and (later) file tags.

I really need that script, and I’m sure other users will need it too.


There’s an “end;” missing to close the try…except block, but the rest looks fine.

Yeah, I noticed that missing ‘end’ myself later, and the code I finally wrote was slightly different, but it all works perfectly!


When I process a Playlist with about 40+ items in it (writing to Database), it locks up mAirList for several minutes, so I get the ‘Application seems to be frozen’ dialog. :o

Hence my next question: is there any equivalent in mAirListScript to the DoEvents statement in MS Visual Basic 6? That statement allows the rest of Windows to literally ‘do all pending events,’ preventing a VB app from hogging the entire PC while it is running. If there is such a statement, I could put it in my loop and thus ‘un-freeze’ mAirList after each database write completes.

Thanks again in advance!

(PS: Yes, this is on my ‘old’ PC, as you call it. But I get the same problem on my ‘new’ PC as well: that machine has a 3.40GHz processor and 3GB of RAM.)


There is an equivalent, namely Application.ProcessMessages, but that’s not useful here, because depending on the way they are activated, scripts may run in a separate thread anyway.

Do you run the script from the menu in this special case?

Yes, I’m running the script from the mAirList Action menu. :slight_smile:

I will give Application.ProcessMessages a try in any case, and will let you know whether it makes any difference.


Ehm, I don’t think it will work from a script, because it’s not imported. It’s not a good idea to call it anyway, because it might crash your system when the scripts happens to be run in a background script (e.g. when called through the event scheduler) - this is because Delphi can only process window messages from the main thread.

If I recall correctly, Pascal Script (the 3rd party component I’m using for the scripting) can call ProcessMessages itself between each step of execution. I will investigate this possibility (and only enable it for scripts executed in the main thread).

Just be aware of the concurrency issues this will imply. For example, when you loop through the playlist from 0 to GetCount-1, and you allow window messages to be processed during this loop, the user might delete an item while your script executes, and your loop will crash. (This can already happen today when the script is executed in the background.)

All relevant objects offer BeginRead/EndRead and BeginWrite/EndWrite operations that allow you to lock an object while you process it. On the other hand, this might block the GUI again, because a redraw function might have to wait for the playlist to be unlocked again.

True: I discovered that myself (!).

Just to clarify what you mean by ‘main thread:’ does that include scripts called by Open, Run script in the main toolbar, and scripts run from the Action menu in the main toolbar? But yes, that would be good. :slight_smile: As an example, the script I’m writing ‘stops’ the LED clock if called from the Action menu, and leaves the Open dialog showing if run using Open, Run script.

It also seems to be the case that SystemLog message don’t get written until the updates again after ‘stopping,’ so the ‘Please wait …’ message I display at the start of the script doesn’t show at all, and only the ‘Processing complete’ message’ is ever seen in the ‘staus bar’ area. Of course, all messages DO get correctly written to the System Log. :slight_smile:

I hear what you are saying about the concurrency. Most of the scripts I write are scripts which process an entire Playlist in some way or another (like IVP or the current one). I hope (?) that anyone else using those scripts would not be so silly as to change the Playlist contents while those scripts are running?!!


Delphi processes all GUI-related things, including GUI updates and processing of user input, from a single thread (the so-called VCL main thread). This is why everything freezes while the script is running.

Script activated through events or remote control are different, because that code is run in a separate thread anyway.

Another possible solution to the GUI freezing is to run scripts always in a separate thread, even when started through the menu. In my opinion, that’s more favorable than the ProcessMessages thing, because it keeps the call stack clean (no nested calls of the window processing routines).

Regarding concurrency, be aware that the playlist may not only be modified through user input but also through other mechanisms running in the background, e.g. the automation or events. It’s always a good idea to use locking where possible (but avoiding to lock the objects longer than needed, because it will block other threads).

I agree with you about always running scripts in a separate thread: that sounds good. :slight_smile:

I just looked for IPlaylist.BeginRead/EndRead in the scripting help and it’s not there. Am I looking in the wrong place?

Also, bear in mind that in my case, this would lock the Playlist for maybe four or five minutes. Would that cause any problems?

Thanks again.


You have to walk up the interface hierarchy a bit: IPlaylist -> IPersistentList -> IBaseList -> IUpdateObject, there you are :slight_smile:

By the way, it’s not BeginWrite/EndWrite but rather BeginUpdate/EndUpdate here. Use these methods if you want to make changes to the playlist (move or delete items etc.). All write operations encapsulated in BeginUpdate/EndUpdate will also be cached and “processed” only once at the end. For example, the automation will not update itself until you call EndUpdate, and the playlist will not redraw until then. The BeginUpdate/EndUpdate mechanism is a key concept in mAirList’s internal asynchronous communication system.

Regarding the locking, for really long operations that only read the playlist but don’t modify it, I would consider creating a copy of the playlist and working on that copy instead. There’s two ways to create a such a copy, assuming that copyPlaylist is declared as an IPlaylist variable:

  1. Use Clone:
playlistCopy := IPlaylist(CurrentPlaylist.Clone);

(Note: The Clone() method is defined in the abstract IPersistentList interface which is also used for Action Lists, Event Lists etc. - that’s why it returns an IPersistentList reference which needs to be typecasted into IPlaylist manually.)

  1. Create a new playlist and use Assign or Copy:
playlistCopy := Factory.CreatePlaylist;
playlistCopy.Assign(CurrentPlaylist); // alternative A
playlistCopy.Copy(CurrentPlaylist); // alternative B

What’s the difference between Assign and Copy? Assign will only copy the references to the items over to playlistCopy, so you can still access (and modify) the original items through your copy of the playlist. Copy will also create cloned copies of each individual item. (Note: CurrentPlaylist.Clone uses Copy internally, so you will also get a cloned copy that way.)

All understood, so it looks like ASSIGN is the best way to go for what I’m doing (updating each Item’s TYPE, then saving the updated version to database and/or MMD and/or file tag).

Thanks again! ;D


Good news: The create/assign statements are working AOK.

Bad news 1: The mAirList GUI is still ‘locked out’ when I run the script.

Bad news 2: Making the copy (using .Assign) does not ‘protect’ the Playlist. I can still delete an item (at the end of the Playlist in this case), and then the script does not process it. I assume (?) this is because the Assign method passes references; in which case, would I be better to use Clone (or Copy) instead?