importing spotlogs into mAirlist playlist

Hey dudes,

This is my second post here, my introduction was http://forum.mairlist.com/index.php/topic,5529.0.html

… absolutely L.O.V.I.N.G. mAirlist after a few days of tinkering with it now, never knew it was so versatile and deep, … AWESOME job, Torben !

I wonder, is there a way to make a script for merging our spotlogs into mAirlist playlists :

We would let mAirlist schedule the music on the one hand (scheduling “commercial break” after each song) and have it “importing” the spotlogs from our traffic software on the other hand.

Breaks after each song because we need to be able to play a spot after each song in some cases, so better to set it up in this way …

These traffic logs are TXT files (1 TXT for each day) looking like this :

20,“11:20”
11,“01”,01,0,“Block opener”
11,“52”,01,0,“mc donalds”
21

  • 20 being the “code” for start block / “11:20” being the name of the block (20 minutes past 11)
  • 11 being the “code” for play this spot / “01” being the filename of the spot (01.mp3) / (the rest of the line can be ignored) the 01 and 0 as I don’t really know what these mean, and the last one being a “remark”-field … (maybe good to import as “campagne name”
  • next line being spot “52.mp3”
  • 21 being the “code” for end block

Is there a way to get these spotlogs into the mAirlist playlist by using a script (launching it at the moment of loading the playlist for the next hour) so everything is last-minute up to date for the next hour …

I heard that working with an m3u file would be a better way to go because mAirlist scripting is focused towards that, but as our traffic software can’t export m3u, we would maybe also need a “txt-m3u batch converter” on our traffic computer, to convert the traffic output into m3u before uploading it to the mAirlist server (in the cloud).

Do we need to use “containers” or not ? I don’t care as the station will be 100% automated anyway, no DJ’s

let me know what you programming gurus think !

Greetings

Dirk

Hi Dirk,

this should be fairly easy. mAirListScript can handle CSV files very well. I will post a code fragement later.

I would prefer using containers. It doesn’t really make a difference for an automated station, but the advantage of containers is that mAirList will never “break them apart” when soft-fixed time events occur.

One thing that’s still unclear is: How will the script identify the position at which the block is going to be inserted? Looking at the starting times isn’t really so easy. From the script’s point of view, the most convenient solution is placing dummy elements (e.g. a short silence audio file) into the music log, possibly named like the traffic block to insert, which the script replaces by the actual block. Would that be possible?

Torben

[quote=“Torben, post:2, topic:7690”]One thing that’s still unclear is: How will the script identify the position at which the block is going to be inserted? Looking at the starting times isn’t really so easy. From the script’s point of view, the most convenient solution is placing dummy elements (e.g. a short silence audio file) into the music log, possibly named like the traffic block to insert, which the script replaces by the actual block. Would that be possible?

Torben[/quote]

Hey Torben,

that was exactly what I had in mind, placing a dummy in the playlist, referring to the block that it needs to be replaced by …

this dummy would have to reffer to the same as the “block time” in the TXT file, so we don’t get the wrong blocks at the wrong time (in case some are empty)

wathc out though with “short silence audio file” because what’s going to happen if there is no commercial scheduled for a certain “block”, will it leave the “short silence” ? thus messing up the smooth mix into the next song ? or will it be removed (because there’s no commercial to be played) thus letting mAirlist make a perfect mix to the next song, like the commercial block was never there …

Greetings

Dirk

The answer to your question depends on exactly what your final script does; and Torben did say ‘such as a short silence element.’

Another way to indicate the place where a set of ad. spots ‘might be’ would be to add a DUMMY item, edited so that it has a specific Artist. Assuming you are using mAirListDB, the way I would tackle that is to open mAirListDB and do this:

[ol][li]Create a new virtual Folder for each possible ad. spot in an hour. Name each folder (for example) AdSpot1, AdSpot2, etc.[/li]
[li]Make sure AdSpot1 is the current selected (clicked) Folder, then click New Item, Dummy.[/li]
[li]In the Artists field on the Properties dialog of the new Dummy item, type AD SPOT 1,then click OK.[/li]
[li]Repeat the last two steps for each Folder you created in step 1. You should now have one Folder for each possible ad. spot in your hour, with a single DUMMY item in each Folder, each with Artists the same as the Folder name.[/li]
[li]Amend your Hour Template(s) to add Items for AdSpot1, AdSpot2, etc. at the right place in the hour. Make sure each of these Items is fixed and not optional.[/li][/ol]

When you generate Database Playlists from these Hour Templates, you will have DUMMY elements in each hour’s Playlist at each point where there could be an ad. break. Because there is only one Item in each Folder, the Mini Scheduler will always insert that specific Item when that Folder is in an Hour Template. (This technique is useful generally, to ‘force’ a specific Item into a Playlist. ;))

These Items can easily be found by a mAirList script later, and positively identified as an ‘ad. spot placeholder’ by its Artists (does the Artists of this DUMMY Item equal ‘AD SPOT something’?). So if you use DUMMY Items for anything else in your Playlist (like top of hour markers, for example), the script won’t replace ‘other’ DUMMY Items with an ad. spot. :wink:

You don’t have to use numbers (AD SPOT 1): anything in a sequence (A, B, C) would be fine. Or, if you don’t need to be so specific, you could use just one AD SPOT Item; but having a sequence means you could (for example) have ad. spots 2 and 4 of four per hour ‘populated’ and spots 1 and 3 empty. It would be much harder to do that without some kind of unique identifier per potential spot within the hour!

Finally, I’m fairly sure that mAirListScript can process Database Playlists directly (right, Torben?). If so, you could for example have a mAirList script which you run once a day at maybe 2300, to ‘insert’ the ads. for the following day directly into your Database Playlists.

Torben is right about using the times being a possible problem. Maybe your ‘pre-processing’ script could use some kind of ‘time block’ to work out which ad. set should go into which spot in an hour. So let’s say three spots per hour, ‘ideally’ at 10, 30, and 50. Any break with a start time from 00-20 (minutes) would be spot 1, 20-40 would be spot 2, and >40 would be spot 3. A system of that sort should work in most cases. Obviously, the closer you can ‘synchronise’ your ad. scheduling system so that all your ad. break files are generated with a start time close to your ‘ideal’ times of ad. breaks in mAirList, the better this system will work!

Hope that helps.

BFN
Cad

Usually, a Traffic Merge is done by placing an Exact Time Marker in the music-log that is then matched with the corresponding advert-break. Using a Time format is the way that it’s been done for some time. Dirk’s example advert template looks like a SuperImpexx-style format that Dalet would take: opening block, content, end-of-block. Typically, ad-logs comprise of the following syntax:

13:20:00 12345 PIZZA SHACK
13:20:00 54321 USED CARS
13:40:00 98765 FAST FOOD
13:40:00 56789 XMAS SHOPPING

The bare essentials are the scheduling time and the cart/filename, the advert name optional although useful for debugging via Notepad. The merge routine simply places any matching lines (13:20 or 13:20:00) into the position of the matching Exact Time Marker. Some playout systems allow configuration for this using a %token% system or string construction (ie: field postion+length).

sorry been on an other project all weel, last week, so I couldn’t answer

yeah, it looks great ! that would work just fine for us !

how would we start making this script ?

is there something we can make some changes too, or do we need to start from scratch ?

please let me know, I’m on it again this week !

:slight_smile:

I have put together a script that should do pretty much what you want:

  • It scans the contents of the playlist for items with artist=$TRAFFIC, and title=<name/time of the block> - these items can be placeholders, dummies, 0.1s-silence-audios or whatever.

  • For each found item, it scans the traffic log (saved as yyyy-mm-dd.txt in a given directory) for a block with the same name/time, creates a container from the spots, deletes the placeholder, and inserts the container instead.

The artist/title of the container is set to the same values as the placeholder. That means that you can run the script over and over again, it will replace existing containers (created during earlier runs) with the latest version from the traffic log.

All you have to do is to make sure that your music log contains the placeholders with the correct artist and title.

Are you using M3U as the import format for the music log? If so, it is possible to add some magic lines to them in order to add dummy items.

function CreateTrafficContainer(trafficLog: TStringList; title: string): IContainerPlaylistItem;
var
  i, j: integer;
  line: TStringList;
  fpi: IFilePlaylistItem;
begin
  // Create an empty container
  Result := Factory.CreateContainerPlaylistItem;

  // Set a few properties
  Result.SetTitle(title);
  Result.GetArtists.Add('$TRAFFIC');
  Result.SetItemType(pitAdvertising);

  // The "line" is used to split each line into its comma-delimited components
  line := TStringList.Create;
  try
    // Walk through the traffic log, looking for the start tag
    for i := 0 to trafficLog.Count - 1 do begin
      // Split line
      line.CommaText := trafficLog[i];

      // start tag?
      if (line[0] = '20') and (line[1] = title) then begin

        // parse the remainder of the block
        for j := i + 1 to trafficLog.Count - 1 do begin
          // Split line
          line.CommaText := trafficLog[j];

          // End of block? exit here
          if line[0] = '21' then exit;

          // spot?
          if line[0] = '11' then begin
            // Create a playlist item for the spot
            fpi := Factory.CreateFilePlaylistItem('c:\spotdirectory\' + line[1] + '.mp3', []);
            fpi.SetTitle(line[4]);
            fpi.SetItemType(pitAdvertising);

            // Add spot to container
            Result.GetPlaylist.Add(fpi);
          end;
        end;
        exit;
      end;
    end;

    // Is the execution reaches this line, no block was found
    SystemLog('Warning: did not find any spots for ' + title);
  finally
    line.Free;
  end;
end;


var
  trafficLog: TStringList;
  i: integer;
  cpi: IContainerPlaylistItem;

begin
  trafficLog := TStringList.Create;
  try
    // Load traffic log into string list
    trafficLog.LoadFromFile('c:\temp\' + FormatDateTime('yyyy-mm-dd', now) + '.txt');

    // Lock playlist while the script runs
    CurrentPlaylist.BeginUpdate;
    try
      // Walk through playlist looking for traffic placeholders
      for i := 0 to CurrentPlaylist.GetCount - 1 do
        if CurrentPlaylist.GetItem(i).GetArtistsString = '$TRAFFIC' then begin
          // found a $TRAFFIC item!

          // Generate container from the traffic log,
          // using the placeholder title as the block name
          cpi := CreateTrafficContainer(trafficLog, CurrentPlaylist.GetItem(i).GetTitle);

          // Delete the placeholder, insert the container
          CurrentPlaylist.Delete(i);
          CurrentPlaylist.Insert(i, cpi);
        end;


    finally
      CurrentPlaylist.EndUpdate;
    end;
  finally
    trafficLog.Free;
  end;
end.

holy shit, how AWESOME is that ?

I’ll try it as soon as I can …

Torben : YOU THA MAN !

Dirk

no, I’m using the mini scheduler of mAirlist …

by the way is there a command to automatically schedule playlists for tomorrow in the mini-scheduler, which I could put in the “timer” ? (to make mAirlist schedule it’s own playlists for the next day … (can I even select which hours to schedule - as I’m not a 24/7 station) ? just wondering …

Yes, there’s an action “Database -> Generate playlists” that can be used in an (e.g. hourly) event. You can specify the number of hours to generate playlists for in advance.

The action works just like the Mini Scheduler dialog, only automated. That means that it uses the template hour assignments for the weekday/hour as specified in the admin configuration.

Ah! You’re using Mini Scheduler!

Yes, Dirk: whether you ‘bulk process’ a week or more of playlists in advance, or whether you generate each one ‘on the fly’ as Torben mentions, you ultimately use an Action in an Event.

Events are a very powerful part of mAirList, and you can easily set up ‘irregular’ or even one-off Events. The Manual explains Events in detail, with practical examples of how to create Events to suit irregular or non-24/7 schedules.

So, there are two ways to go:

  1. If you need to see and ‘tweak’ playlists in advance, ‘bulk’ generate them maybe a week in advance in the Mini Scheduler. This method makes it easy to hand out or send HTML files of each playlist to your presenters in advance, to help them ‘prep.’ their shows. Using this method, your Events will run ‘Load/Play database playlist’ Actions.

  2. If you just need playlists churned out, you can ignore the Mini Scheduler and instead use Torben’s idea of running ‘Generate playlist’ Actions in your Events, in addition to the ‘Load/Play database playlist’ Actions to actually load these into the main Playlist.

The ‘right’ way is the one that suits you best. This is true of just about any feature of mAirList. It’s a constantly surprisingly mature and capable product which is based on solid ‘on the air’ experience with it by its many satisfied users. Many of those features were originally suggested by mAirList users, and were debated here before being implemented by Torben. mAirList users are not shy to say if there’s something they don’t like, or which doesn’t work in a way which suits them. They are also very friendly and enthusiastic ‘radio people,’ who are always happy to help and share their knowledge and their scripts.

BFN
Cad

Can I ask a scripting expert if it is possible to hold/store a flag in memory (or as a file stored on disk I suppose) which can be used in subsequent script runs?

To explain – I like the idea of substituting a $traffic item in the playlist with actual audio using a script run when the playlist is loaded but want to cycle through a number of predefined breaks (break1, break2, break3 etc).

My thoughts are to substitute the first ‘$traffic’ flag with Break1, the second ‘$traffic’ flag with Break2 etc but if there are only 2 ‘$traffic’ flags in the hour then the first ‘$traffic’ flag in the following hour should start with Break3 so I need to store the next break number in the sequence for use when the script is next run (probably the next hour). As a small extra when the next in sequence does not exist (i.e. the file is not present - break4 in the example above) then break1 should be used and the whole process repeated.

I am in the process of ‘experimenting’ with the Torben’s script posted previously but I am stuck on this point.

Any pointers would be appreciated

Ron

You can make use of SetRuntimeData and GetRuntimeData (type string).
It’s documented in the changelog for 3.1.8

regards:
-Serge-

Sounds just what is needed - unfortunately this scripting lark is still very much ‘trial and error’ to me mostly error!

Any chance of a quick example code on how SetRuntimeData and GetRuntimeData works?

Is there a file explaing the basics of scripting and listing what commands are available in the scripts?

Cheers

Ron

// Setting the data
SetRuntimeDate('MyFlag', 'yes');

// reading the data
if GetRuntimeData('MyFlag') = 'yes' then begin
  // do something
end;

Cheers Torben.

I am sure it is straight forward but could I ask a small favour that you expand this so that it is a simple self-contained script that will run as I seem to be having problems with GetRuntimeData not being defined.

This is purely down to my lack of pascal script knowledge which will hopefully improve!

Many thanks again

Ron

To answer your questions:

There is no list of what commands are available in mAirListScript. There is the mairlistscript.CHM file, but you need to understand Pascal-style scripting for that to be useful to you.

You do need to SET runtime data before you try to GET it. But I thought (correct me if I’m wrong?) that you use v3 of mAirList? If so, I don’t think those commands (SetRuntimeData/GetRuntimeData) exist in v3: they’re only present in v4 as far as I know. No doubt Torben will correct me if that’s wrong!

BFN
Cad

Cad

These commands were introduced in 3.1.8 BUT I think you have solved my problem as I am only running 3.1.7!

I will update later tonight and try again!

Cheers

Ron.

Edit: Tried it with v3.1.9 snapshot and it works. Onward and upward …

Gosh! :-
(Don’t take it serious)

:smiley:

Firstly, thanks for the scripting help so far…

I think I am getting the hang of it and have managed to modify Torben’s original script to replace $TRAFFIC markers with ad breaks (probably not pretty but included below). Currently it just cycles around four mp3 files which contain all the adverts for the break in a single file but I kept the container arrangement as I will probably expand it to individual files based on a schedule later on.

And so to my latest script questions …

  1. No duration appears on the replacement entry until it reaches the ‘next’ position in the playlist. Is there any way to calculate this at the time of insertion so that the backtiming display is accurate

  2. When the replacement entry reaches the ‘next’ position and the time is inserted it is the time of the complete file and not a time with ‘autocue’ applied therefore it plays silence at the end of the file. Is there any way of applying autocue?

Many thanks as always

Ron.

[code]const
AUDIOPATH = ‘S:\Audio\Adverts’;

function CreateTrafficContainer(title, audiofile: string): IContainerPlaylistItem;
var
fpi: IFilePlaylistItem;

begin
// Create an empty container
Result := Factory.CreateContainerPlaylistItem;

// Set a few properties
Result.SetTitle(title);
Result.GetArtists.Add(’$TRAFFIC’);
Result.SetItemType(pitAdvertising);

try
// Create a playlist item for the spot
fpi := Factory.CreateFilePlaylistItem(AUDIOPATH + audiofile + ‘.mp3’, []);
fpi.SetTitle(title);
fpi.SetItemType(pitAdvertising);

 // Add spot to container
 Result.GetPlaylist.Add(fpi);

finally
// Can’t think of anything!
end;
end;

var
i, BreakNo: integer;
cpi: IContainerPlaylistItem;

begin
// Lock playlist while the script runs
CurrentPlaylist.BeginUpdate;
try
// Walk through playlist looking for traffic placeholders
for i := 0 to CurrentPlaylist.GetCount - 1 do
if CurrentPlaylist.GetItem(i).GetArtistsString = ‘$TRAFFIC’ then begin
// found a $TRAFFIC item!

    // Start sequence (will action when first run))
    BreakNo := StrToInt(GetRuntimeData('BreakNo')); 
    If GetRuntimeData('BreakNo') = '' then BreakNo := 1;
      
    // Log actions
    SystemLog('Item ' + IntToStr(i) 
      + ' of ' + IntToStr(CurrentPlaylist.GetCount - 1) 
      + ' with Artist ' + (CurrentPlaylist.GetItem(i).GetArtistsString)
      + ' as Break ' + IntToStr(BreakNo));
      
  // Generate container 
  cpi := CreateTrafficContainer('Break ' + IntToStr(BreakNo), 'ABREAK ' + IntToStr(BreakNo));
  // Only one file at the moment just to prove the concept! 

  // Delete the placeholder, insert the container
  CurrentPlaylist.Delete(i);
  CurrentPlaylist.Insert(i, cpi);

  BreakNo := BreakNo + 1;
  If BreakNo = 5 then BreakNo := 1;
  SetRuntimeData('BreakNo', IntToStr(BreakNo));

  end;

finally
  // Unlock playlist
  CurrentPlaylist.EndUpdate;
end;

end.[/code]