Add advertisements and news

Yes, Tony: I did do that in my original ‘Container’ version of the script:

[quote=“Cad, post:14, topic:4993”] // reference the entire Container as a PlaylistItem and title it
adsContainerAsPlaylistItem := IPlaylistItem(ads);
adsContainerAsPlaylistItem.SetTitle(
'Ads. before ’
+ FormatDateTime(‘hh’, nextHour)
+ ‘:00 News’);
[/quote]

… or did you not notice that the first time around? :wink: (I added the bold attributes while quoting it.) The title generated by that line would be in the form Ads. before 21:00 News, using the ‘next hour.’

Note that the ‘Container’ version of the script does not put the News in the Container, it just puts all the ads. in the Container. Of course, the script could be easily changed to do that if that’s what you’d prefer.

BFN
CAD

Hi Cad, thanks again for your time and patience. No I had not noted you had already set a title to display a description for the container but should have realised or asked if it was something that could be done.

Hopefully in future I will study the scripts more closely and come up with one as useful myself.

Kind Regards Tony

Or you could always try running the scripts (making suitable changes for folder names etc.)! :smiley:

Well, in general, you spec. it and I’m happy to code it; but if you also have many years of object-oriented programming experience under your belt, I’m sure you can pick up mAirList scripting from the mAirListScript.chm Help file! ;D That, and yon terribly handy Delphi Basics site, is what I use to learn the lingo.

The main thing to remember is that mAirList scripting is NOT repeat NOT the entire Delphi language, so one often looks on Delphi Basics, finds some useful statement, then finds it doesn’t exist in mAirList scripting. This isn’t Torben’s fault, it’s down to the guy who wrote the component Torben uses; that component does not have any full documentation, and has not been updated/revised for a year or three, but is still ‘first in a field of one’ for adding user scripting to one’s program (e.g. mAirList!).

BFN
CAD

HI Cad,

Great job you did with the script! It’s working as expected. I have only one small problem with it: when there is no ‘ads’ playlist for the specific hour, the script stops by saying that it can’t load the ads playlist. Sure… there is no ads playlist for that hour (we only have some small amount of ads (sadly all non commercial…) which means not at all hours). Would it be possible to build something like {if - else} into the script? =>

[code]If (there’s an ads playlist),
take it;

else (forget it).
and go on with the script;[/code]

No, i’m not a programmer but it’s just an idea ::slight_smile:

Regards
Vincent

I’ll need to have a play and see if mAirList Script has a try/catch/finally kind of construct (unless Torbedn wants to tell me to save time? ;).

If it does, then yes: very easy to ‘snag’ errors like that.

BFN
CAD

Cad,

Maybe this forum helps…
Also a sort of if/else (if/then)…

Vincent

begin
  try
    CurrentPlaylist.GetItem(712);
  except
    SystemLog('Error: ' + ExceptionParam);
  finally
    SystemLog('finally');
  end;
end.

Thanks, Torben! ;D

I had found the ‘magic formula’ at DelphiBasics.co.uk already, just hadn’t done the ‘trial and error’ with PascalScript yet.

So, Vincent, I’ll add that to those scripts and write messages to SystemLog on failure.

Torben: is there any way you could ‘wrap’ standard Delphi Messagebox functionality into a mAirList class which we could access from PascalScript? It would need the usual MsgBox parms., plus I think a boolean ‘modal dialog’ switch would be useful. Do you think that could be made to work? I’ve often said it would be VERY helpful to us script developers to be able to display a message box when necessary (and ideally have both a proc and func version so you can have some user interaction via a Yes/No button for example).

My thinking is: ‘well, mAirList itself can display message boxes, so it must (?) be possible to put a user class around that?’

BFN
CAD

Yes, that should be possible.

OOOH! HAPPY Cad!

Seriously though Torben, it would be excellent to have MessageBox ‘restored’ to usability within mAirList script!

BFN
CAD

With that ‘nudge’ from Torben, here is a new version which checks ALL files when trying to load them. Please note this is also the version which loads. the ads. as a CONTAINER. If anyone needs a ‘load ads. as separate files’ version, please let me know and I’ll write that as well. :slight_smile:

[code]// AdsAndNews v2.1
// Original posted by Vincent Volmer
// New version by Cad Delworth

// v2.1: Added try…except code to catch all file loading errors

const
// ADS_DIR = 'D:\TestRadio\Ads';
// JINGLES_DIR = 'D:\TestRadio\Jingles';
// MUSIC_DIR = 'D:\TestRadio\Muziek';
// NEWS_DIR = 'D:\TestRadio\Nieuws';

ADS_DIR = 'C:\Documents and Settings\Cad.TARDIS\My Documents\mAirList\Testing';
JINGLES_DIR = 'C:\Documents and Settings\Cad.TARDIS\My Documents\mAirList\Testing\News';
MUSIC_DIR = 'C:\Documents and Settings\Cad.TARDIS\My Documents\mAirList\Testing\Music';
NEWS_DIR = 'C:\Documents and Settings\Cad.TARDIS\My Documents\mAirList\Testing\News';

NEWS_OPENER = ‘news-open.mp3’;
NEWS_CLOSER = ‘news-close.mp3’;

// Seconds before the hour to play the News Opener
NEWS_DELAY = 7;

// Name of script, used as a prefix to all SystemLog messages (default: 'AdsAndNews-2.1 script: ')
// If you change this, keep a colon and space as the last two characters.
SCRIPTNAME = 'AdsAndNews-2.1 script: ';

var
ads: IContainerPlaylistItem;
adsPlaylist, newPlaylist: IPlaylist;
adsContainerAsPlaylistItem, piNews: IPlaylistItem;
nextHour: TDateTime;
delay: single;
sFileName: string;

procedure FileErrorMessage(f: string);
begin
SystemLog(SCRIPTNAME + 'WARNING - could not load file ’ + f + ‘.’);
end;

procedure AddCurrentPlaylistItemFromFile(f: String; b: boolean);
begin
try
CurrentPlaylist.Add(Factory.CreatePlaylistItemFromFile(f, b));
except
FileErrorMessage(f);
end;
end;

function MakePlaylistFromFile(f: string): IPlaylist;
begin
result := Factory.CreatePlaylistFromFile(f);
end;

begin

// calculate next hour as Delphi TDateTime
nextHour := (trunc(now * 24) + 1) / 24;

// create a new Container Item
ads := Factory.CreateContainerPlaylistItem;

// put the ads. in it
sFileName := ADS_DIR + FormatDateTime(‘yyyy-mm-dd-hh’, now) + ‘.m3u’;
try
newPlaylist := MakePlaylistFromFile(sFileName);
adsPlaylist := ads.GetPlaylist;
adsPlaylist.AppendPlaylist(newPlaylist);
// calculate when the advertising must start,
// accounting for the seconds-before-the-hour delay
delay := (adsPlaylist.GetDuration / 10000000) + NEWS_DELAY;
// now convert to TDateTime
delay := delay / (24 * 60 * 60);
// reference the entire Container as a PlaylistItem and title it
adsContainerAsPlaylistItem := IPlaylistItem(ads);
adsContainerAsPlaylistItem.SetTitle(
'Ads. before ’
+ FormatDateTime(‘hh’, nextHour)
+ ‘:00 News’);
// Set fixed start time for entire Container
adsContainerAsPlaylistItem.SetStartTime(sttFixed, nextHour - delay);
// Add Container PlaylistItem to current Playlist
CurrentPlaylist.Add(adsContainerAsPlaylistItem);
except
FileErrorMessage(sFileName);
end;

// Add News Opener to Playlist
AddCurrentPlaylistItemFromFile(JINGLES_DIR + NEWS_OPENER, true);

// insert News
sFileName := NEWS_DIR + FormatDateTime(‘yyyy-mm-dd-hh’, nextHour) + ‘-News.mp3’;
piNews := Factory.CreatePlaylistItemFromFile(sFileName, false);
if piNews.ErrorCheck = true then
begin
// set fixed start on-the-hour for the News, then add to Playlist
piNews.SetStartTime(sttFixed, nextHour);
CurrentPlaylist.Add(piNews);
end
else
FileErrorMessage(sFileName);

// Insert News Closer
AddCurrentPlaylistItemFromFile(JINGLES_DIR + NEWS_CLOSER, true);

// Paste music log for next hour
sFileName := MUSIC_DIR + FormatDateTime(‘yyyy-mm-dd-hh’, nextHour) + ‘.m3u’;
try
newPlaylist := MakePlaylistFromFile(sFileName);
CurrentPlaylist.AppendPlaylist(newPlaylist);
except
FileErrorMessage(sFileName);
end;

end.
[/code]

If you’re writing scripts, please note the following points about ‘trapping’ file loading errors:

[ul][li]CurrentPlaylist.Add(Factory.CreatePlaylistItemFromFile(f, b)) WILL raise an Exception (give you an error) if the file (f) isn’t found, so you CAN put that statement within a try block.[/li]
[li]pi := Factory.CreatePlaylistItemFromFile(f, b) WILL NOT raise an Exception (give you an error) if the file (f) isn’t found, so you CANNOT put that statement within a try block: which is why you need to use if pi.ErrorCheck = true afterwards (false means the PlaylistItem has an error = the file didn’t load).[/li][/ul]

Just in case you were wondering why the code was written in that way. :wink:

The above script has been tested and carries my usual guarantee that it will ‘do exactly what it says on the tin’ (UK catchphrase, Vincent, don’t worry!).

BFN
CAD

Hi cad, yes please we would like a non-container version of this script.

Kind Regards tony

HI Cad,

I only found 3 changes/wishes to make the script more -fool proof- and time stable.

b[/b] When there are no Ads the news-opener is not placed at the right time (start News ##:00:00):

b[/b] When there’s no News (for what reason) the news-open and news-close must be ignored and not being played.
The start of playing the first track of the next hour playlist must be on ##:00:00.


(3)
Also when no Ads and no News the news-open and news-close must be ignored and not being played.
The start of playing the first track of the next hour playlist must be on ##:00:00.

I hope you understand what I mean and find it useful enough to implement it into the script :wink:

Thanks Cad!

Vincent

Just for your information: mAirList 3.0 will support “reverse backtiming”, which means that the item is back-timed so that it ends at the given time (rather than starting at the time). This should be very helpful for this kind of application.

Thanks Torben… would be great!

Vincent

Cad,

After testing some hours I found a problem: after loading the Ads container the duration is about 33 seconds (that’s what is visible in the playlist). When I look for its properties I see that the open en close jingles are not part of this duration. That’s the reason that the news-open jingle is ignored (skipped).

Loading from the script

When I load the container back from the Recycle Bin into the playlist, the duration is different but correct.

OK Vincent, I think I understand all your requirements. I may need to ask some more questions as we solve this together. Some of your deductions about the problems are not quite correct, but that does not matter. :slight_smile:

Before I work on all those problems, a question to Torben: is there a PascalScript (or mAirListScript) statement which tests for the existence of a specified file/path? It would be very helpful to be able to write code like:

var
  FileFlag: boolean;
begin
  FileFlag := FileExists('c:\<whatever>\<blah>.mp3');
end.

or

begin
  if FileExists('c:\<whatever>\<blah>.m3u') then
    SystemLog('The file exists.')
  else
    SystemLog('The file does not exist.');
end.


BFN
CAD

Have you tried that code? The corresponding Delphi function is in fact FileExists, but I don’t know if it’s imported into Pascal Script by default. If not, I can do that.

I’ll try it here and get back to you, Torben. :slight_smile:

[later]

YES! FileExists does indeed work. 8)

[later still]

If you’d like to know the Delphi functions that would be VERY useful to have exposed in mAirListScript ;), those would be:

[ul][li]DecodeDateTime and EncodeDateTime[/li]
[li]These Inc… functions: IncDay, IncMinute, IncMonth, IncSecond, and IncYear[/li]
[li]InputBox[/li]
[li]IsLeapYear[/li]
[li]MessageDlg[/li]
[li]StrToDateTime (FormatDateTime makes StrToDateTime redundant ;))[/li][/ul]

None of these are available in mAirList scripting right now, and all would be useful (but if you can’t do all of them, drop IsLeapYear first! :D).

It does seem silly that so many objects in mAirList use dates and times, but mAirListScript does not currently support most of the Delphi date/time functions (!); so script coding is often more, er, ‘challenging’ than it needs to be.

BFN
CAD

[quote=“Vincent Volmer, post:33, topic:4993”]I only found 3 changes/wishes to make the script more -fool proof- and time stable.

b[/b] When there are no Ads the news-opener is not placed at the right time (start News ##:00:00)
b[/b] When there’s no News (for what reason) the news-open and news-close must be ignored and not being played.
The start of playing the first track of the next hour playlist must be on ##:00:00.
b[/b] Also when no Ads and no News the news-open and news-close must be ignored and not being played.
The start of playing the first track of the next hour playlist must be on ##:00:00.

I hope you understand what I mean and find it useful enough to implement it into the script :wink:

Thanks Cad![/quote]

Firstly, you’re very welcome, Vincent! My pleasure.

OK: I have solved all three problems above, plus the problem (for most users) that a Container’s contents are not individually logged—which would be a disaster for anyone playing paid-for ads.! The solution I came up with was to insert the ad. break, from ad. opener (if any) to news closer (if any), as a big ‘chain’ of Linked items instead of a Container.

In ASSIST mode, the entire ‘chain’ of Linked Items plays out through one Player, just like a Container would; in AUTO mode, each item plays in a separate Player (I’ve tried both and that is how it works in practice). This was the best compromise I could think of between wanting the whole break ‘in one Player’ while still having each played Item logged in the mAirList log.

I’ve ‘tightened up’ the code in general, and each element (ad. break, news, music) is now processed by its own procedure which is called from the main code. And as I said earlier, the script now correctly handles every combination of optional ads and news openers and closers, and presence/absence of ads and news files for the following top-of-hour; unless you find otherwise? I gave it some fairly intensive testing here.

// AdsAndNews v2.2
// Written by Cad Delworth (Clearances Manager, Leith FM, Edinburgh, Scotland)
// based on original code and concepts by Vincent Volmer

// The AdsAndNews script adds a pre-News ad break, top-of-hour News, and the next hour's
// Music Log to the current Playlist.  The script sets fixed start times 'intelligently,'
// depending on whether ads and news exist for the next top-of-hour.
// AdsAndNews can thus be run as an hourly scheduled Event for any hour of the day.

const
// Change these directories as required.
  ADS_DIR = 'C:\Documents and Settings\Cad.TARDIS\My Documents\mAirList\Testing\';
  JINGLES_DIR = 'C:\Documents and Settings\Cad.TARDIS\My Documents\mAirList\Testing\';
  MUSIC_DIR = 'C:\Documents and Settings\Cad.TARDIS\My Documents\mAirList\Testing\';
  NEWS_DIR = 'C:\Documents and Settings\Cad.TARDIS\My Documents\mAirList\Testing\';

// Change the file name of any opener/closer which you do NOT use to '' (empty string).
// DO NOT DELETE these constants: if you do, the script will not work.
  ADS_OPENER = 'Ad breaker sting.mp3';
  ADS_CLOSER = 'Ad breaker sting.mp3';
  NEWS_OPENER = 'Leith FM - News In sting.mp3';
  NEWS_CLOSER = 'Leith FM - News Out sting.mp3';

// Name of script, used to prefix all SystemLog messages (default: 'AdsAndNews-2.2 script: ')
// If you change this, keep a colon and space as the last two characters.
  SCRIPTNAME = 'AdsAndNews-2.2 script: ';

// Number of mAirList/BASS time units (10 million) which equals 1 second
  ONE_SECOND = 10000000;
// Number of seconds in one day (for TDateTime conversions)
  SECONDS_PER_DAY = 86400;

var
  bAdsExists, bMusicExists, bNewsExists: boolean;
  sAdsFileName, sMusicFileName, sNewsFileName: string;
  dtNextHour: TDateTime;

procedure FileErrorMessage(f: string);
// Displays a 'file not found' message
begin
  SystemLog(SCRIPTNAME + 'WARNING - file ' + f + ' not found.');
end;

function CheckFileExists(f: string): boolean;
// Returns true if a file exists, false if not (empty string = false)
// Displays a message if file does not exist, UNLESS f is an empty string
var
  b: boolean;
begin
  if f = '' then
    b := false
  else
  begin
    b := FileExists(f);
    if b = false then
      FileErrorMessage(f);
  end;
  result := b;
end;

procedure AddFileToPlaylist(pl: IPlaylist; f: String; b: boolean);
// Adds a (usually MP3) file as an Item at the end of a Playlist
// Displays a message if file does not exist
begin
  try
    pl.Add(Factory.CreatePlaylistItemFromFile(f, b));
  except
    FileErrorMessage(f);
  end;
end;

procedure SetLinked(pi: IPlaylistItem);
// Sets the 'linked to next item' option of a Playlist Item
var
  piOptions: TPlaylistItemOptions;
begin
  piOptions := pi.GetOptions;
  piOptions := piOptions + [pioLinked];
  pi.SetOptions(piOptions);
end;

procedure ProcessAdBreak();
// Adds the ad break and News Opener (if any) to the Playlist as Linked items,
// and backtimes the entire break to end at top-of-hour
var
  i, iMax: integer;
  plAdBreak, plAds: IPlaylist;
  sngDuration: single;

begin

  // Create the Ad Break Playlist to be added to current Playlist
  plAdBreak := Factory.CreatePlaylist;

  // Process the ad break if the file exists
  if bAdsExists then
  begin
    // Create a Playlist of the M3U contents
    plAds := Factory.CreatePlaylistFromFile(sAdsFileName);
    // Add the Ads Opener, if any, to the Ad Break
    if CheckFileExists(ADS_DIR + ADS_OPENER) = true then
      AddFileToPlaylist(plAdBreak, ADS_DIR + ADS_OPENER, true);
    // Add the M3U Playlist to the Ad Break
    plAdBreak.AppendPlaylist(plAds);
    // Add the Ads Closer, if any, to the Ad Break
    if CheckFileExists(ADS_DIR + ADS_CLOSER) = true then
      AddFileToPlaylist(plAdBreak, ADS_DIR + ADS_CLOSER, true);
  end;

  // If News exists, add the News Opener (if any) to the Ad Break,
  // so we can backtime the whole Ad Break (including News Opener) to top-of-hour
  if bNewsExists
  and (CheckFileExists(NEWS_DIR + NEWS_OPENER) = true) then
    AddFileToPlaylist(plAdBreak, NEWS_DIR + NEWS_OPENER, true);

  // Link all items in the Ad Break, except the last item
  // (the Ad Break MAY be followed by Music)
  iMax := plAdBreak.GetCount - 2;
  // Only Link Items if we have at least two Items in the Ad Break Playlist
  if iMax >= 0 then
  begin
    for i := 0 to iMax do
    begin
    SetLinked(plAdBreak.GetItem(i));
    end;
  end;

  // If News will follow, make the last item Linked
  // (this will be the final ad., the Ad Closer, or the News Opener, as appropriate)
  if bNewsExists = true then
    SetLinked(plAdBreak.GetItem(plAdBreak.GetCount - 1));

  // Calculate the Ad Break start time...

  // Convert Ad Break duration to seconds
  sngDuration := plAdBreak.GetDuration / ONE_SECOND;
  // Convert to a TDateTime value
  sngDuration := sngDuration / SECONDS_PER_DAY;
  // Set fixed start time for first item in Ad Break, backtimed from top-of-hour
  plAdBreak.GetItem(0).SetStartTime(sttFixed, dtNextHour - sngDuration);

  // Append the Ad Break Playlist to the current Playlist
  CurrentPlaylist.AppendPlaylist(plAdBreak);

end;

procedure ProcessNews();
// Adds the News (and News Closer, if any) to the Playlist as Linked items,
// with a fixed start of top-of-hour
var
  piNews: IPlaylistItem;
begin
  piNews := Factory.CreatePlaylistItemFromFile(sNewsFileName, true);
  if piNews.ErrorCheck = true then
    begin
      // Set fixed start time on-the-hour for the News Bulletin
      piNews.SetStartTime(sttFixed, dtNextHour);
      // Check whether we need a News Closer
      if CheckFileExists(NEWS_DIR + NEWS_CLOSER) = true then
      begin
        // Link the News to the News Closer
        SetLinked(piNews);
        // Add the News to the Playlist
        CurrentPlaylist.Add(piNews);
        // Add the News Closer to the Playlist
        AddFileToPlaylist(CurrentPlaylist, NEWS_DIR + NEWS_CLOSER, true);
      end
      else
        // No News Closer, add the News to the Playlist
        // (NOT Linked, because Music will be next)
        CurrentPlaylist.Add(piNews);
    end
  else
    FileErrorMessage(sNewsFileName);
end;

procedure ProcessMusic();
// Adds the next hour's Music Log to the Playlist
// If there was no News, give the first Item a fixed start time of top-of-hour
var
  plMusic: IPlaylist;
begin
  try
    plMusic := Factory.CreatePlaylistFromFile(sMusicFileName);
    if bNewsExists = false then
      // No news, so set first Music Item to start on-the-hour
      plMusic.GetItem(0).SetStartTime(sttFixed, dtNextHour);
    // Add the Music to the current Playlist
    CurrentPlaylist.AppendPlaylist(plMusic);
  except
    FileErrorMessage(sMusicFileName);
  end;
end;

// MAIN CODE STARTS HERE //

begin

  // Calculate start of next hour as Delphi TDateTime
  dtNextHour := (Trunc(Now * 24) + 1) / 24;

  // Generate the file names of ads, music, and news for following top-of-hour
  sAdsFileName :=
    ADS_DIR
    + FormatDateTime('yyyy-mm-dd-hh', Now)
    + '-Ads.m3u';
  sNewsFileName :=
    NEWS_DIR
    + FormatDateTime('yyyy-mm-dd-hh', dtNextHour)
    + '-News.mp3';
  sMusicFileName :=
    MUSIC_DIR
    + FormatDateTime('yyyy-mm-dd-hh', dtNextHour)
    + '-Music.m3u';

  // Check whether these files exist...
  // Use a custom function which reports missing files to the user,
  // and store the results to use later on in the script
  bAdsExists := CheckFileExists(sAdsFileName);
  bNewsExists := CheckFileExists(sNewsFileName);
  bMusicExists := CheckFileExists(sMusicFileName);

  // Add the 'existing' elements to the current Playlist

  // News Opener (if any) is handled as part of the ad. break pre-News
  if bAdsExists or bNewsExists then
    ProcessAdBreak;
  
  if bNewsExists then
    ProcessNews;

  if bMusicExists then
    ProcessMusic;
    
end.

I hope that both Vincent and Tony approve of the ‘new! improved!’ v2.2. :smiley:

Enjoy!

BFN
CAD