ionic5 compiling for android problems

Posted on Updated on

One of the most annoying thing about Ionic is chasing your tail.

Things don’t always w2ork as expected. You have to install JavaSDK, Gradle, maybe CocoaPods, tools.jar, and more.

This tool me hours to find the solution:

No installed build tools found. Install the Android build tools version 19.1.0 or higher.
I found the answer here:

The next problem was this:

"Could not find tools.jar. Please check that /Library/Internet Plug-Ins/JavaAppletPlugin.plugin/Contents/Home contains a valid JDK installation."

I found a solution from here:

Here is my output:

stephenmonro@SMMMacMini c4i2021 % /usr/libexec/java_home -V | grep jdk
 Matching Java Virtual Machines (3): (x86_64) "Oracle Corporation" - "Java" /Library/Internet Plug-Ins/JavaAppletPlugin.plugin/Contents/Home
 1.8.0_281 (x86_64) "Oracle Corporation" - "Java SE 8" /Library/Java/JavaVirtualMachines/jdk1.8.0_281.jdk/Contents/Home
 1.8.0_171 (x86_64) "Oracle Corporation" - "Java SE 8" /Library/Java/JavaVirtualMachines/jdk1.8.0_171.jdk/Contents/Home
 stephenmonro@SMMMacMini c4i2021 %

The example went like this:

I used the command /usr/libexec/java_home -V | grep jdk Cristian Gomez provided, got two paths:

Matching Java Virtual Machines (2): (x86_64) "Oracle Corporation" - "Java" /Library/Internet Plug-Ins/JavaAppletPlugin.plugin/Contents/Home
1.8.0_111 (x86_64) "Oracle Corporation" - "Java SE 8" /Library/Java/JavaVirtualMachines/jdk1.8.0_111.jdk/Contents/Home
Changing the .zshrc did not solve the problem.
Finally I copied tools.jar in /Library/Java/JavaVirtualMachines/jdk1.8.0_111.jdk/Contents/Home/lib/ to /Library/Internet Plug-Ins/JavaAppletPlugin.plugin/Contents/Home/lib/ and solved the problem.

Now, to solve the next problem with signing, which should be too hard as well.

Another problem when it wouldn’t compile with the following error:

Task :app:compileDebugJavaWithJavac FAILED
 \platforms\android\app\src\main\java\com\ionicframework\cordova\webview\ error: package does not exist
 \platforms\android\app\src\main\java\com\ionicframework\cordova\webview\ error: cannot find symbol
 symbol:   class RequiresApi
 location: class IonicWebViewEngine.ServerClient
 Note: Some input files use or override a deprecated API.
 Note: Recompile with -Xlint:deprecation for details.
 2 errors

You’ll need to install this plugin:

ionic cordova plugin add cordova-plugin-androidx-adapter

Another problem was this:

in/res/drawable-land-hdpi/screen.png: Error: The drawable "screen" in drawable-land-hdpi has no declaration in the base drawable folder or in a drawable-densitydpi folder; this can lead to crashes when the drawable is queried in a configuration that does not match this qualifier [MissingDefaultResource]

This was solved by this post

Add this in your config.xml under android

<splash density="ldpi" src="resources/android/splash/drawable-land-ldpi-screen.png" />
<splash density="mdpi" src="resources/android/splash/drawable-land-mdpi-screen.png" />
<splash density="hdpi" src="resources/android/splash/drawable-land-hdpi-screen.png" />
<splash density="xhdpi" src="resources/android/splash/drawable-land-xhdpi-screen.png" />
<splash density="xxhdpi" src="resources/android/splash/drawable-land-xxhdpi-screen.png" />
<splash density="xxxhdpi" src="resources/android/splash/drawable-land-xxxhdpi-screen.png" />

One of the bigger things I’ve been noticing with some of the other errors I’m getting, is that once you find the line in code that it failed on, you just do a search, and in this above case, it’s CDVFileTransfer.m

That is the Ionic File Transfer

Just run these two commands associated with that plugin thing, and it then seems to work (until the next error):

ionic cordova plugin add cordova-plugin-file-transfer
npm install @ionic-native/file-transfer

This happens with many of the error 65 problems.

Actually, in this case – a good example of things that don’t always work… I wrote this before succeeding.

However, in the case of the above problem, it didn’t work at all, and so I had to abandon using File-Transfer. Instead, I am now using File-Saver which is half the code and seems to work for PDF files.

I also removed the file-transfer plugins too.

$ ionic cordova plugin remove cordova-plugin-file-transfer
$ npm uninstall @ionic-native/file-transfer

Yet another error. This happens when opening PDFs via the above method:

ERROR Internal navigation rejected – not set for url=’blob:ionic://localhost/ etc

Adding this in the config.xml allows for the opening of the PDF, however it then breaks the app, because if basically opens in an internal webrowser or something weird.

  <allow-navigation href="*" /> 

Stay tuned for a fix...

Yeah, scrap that last stuff. Just use a link like this:

<a style="text-decoration:none;" target="_system" [href]="item.url" [innerHTML]="item.title_date"></a>  

Or try this solution:

 download(url: string, title: string) 

  //// 'hybrid' detects both Cordova and Capacitor
  if ('hybrid')) 
    this.fileTransfer = this.transfer.create();
    .download(url, this.file.dataDirectory + title + ".pdf")
    .then(entry => {
    console.log("download complete: " + entry.toURL());
    .open(entry.toURL(), "application/pdf")
    .then(() => console.log("File is opened"))
    .catch(e => console.log("Error opening file", e));


This is still the error:



The following build commands failed:
CompileC /Users/stephenmonro/Library/Developer/Xcode/DerivedData/Christians_for_Israel-dkgqzlaztjythbaawmdqxxnhjikl/Build/Intermediates.noindex/Christians\ for\\ for\ /Users/stephenmonro/ionicApps/c4i2021/c4i2021/platforms/ios/Christians\ for\ Israel/Plugins/cordova-plugin-file-transfer/CDVFileTransfer.m normal x86_64 objective-c
(1 failure)
Command finished with error code 65: xcodebuild -workspace,Christians for Israel.xcworkspace,-scheme,Christians for Israel,-configuration,Release,-sdk,iphonesimulator,-destination,platform=iOS Simulator,name=iPhone 12 Pro Max,build,CONFIGURATION_BUILD_DIR=/Users/stephenmonro/ionicApps/c4i2021/c4i2021/platforms/ios/build/emulator,SHARED_PRECOMPS_DIR=/Users/stephenmonro/ionicApps/c4i2021/c4i2021/platforms/ios/build/sharedpch
xcodebuild: Command failed with exit code 65
Error: xcodebuild: Command failed with exit code 65
at ChildProcess.whenDone (/Users/stephenmonro/ionicApps/c4i2021/c4i2021/node_modules/cordova-common/src/superspawn.js:136:25)
at ChildProcess.emit (events.js:189:13)
at maybeClose (internal/child_process.js:970:16)
at Process.ChildProcess._handle.onexit (internal/child_process.js:259:5)
[ERROR] An error occurred while running subprocess cordova.

    cordova build ios --release --verbose exited with exit code 65.

    Re-running this command with the --verbose flag may provide more information.

ionic:utils-process onBeforeExit handler: ‘process.exit’ received +0ms
ionic:utils-process onBeforeExit handler: running 2 functions +0ms
ionic:utils-process processExit: exiting (exit code: 65) +36ms

Surprisingly, this worked:

So many problems. So many problems! There is usually a fix somewhere though. You just have to find it.

StationPlaylist Hooks

Posted on

To get your station really singing, I’d suggest using Hooks.

What is a hook you say? A hook is a 10 second snippet of the meat of the song, such as the bit that gets stuck in your head, or a snippet of the chorus or similar.

So, how does this make the station sound good? Well, sometimes you may hear an announcer using the hooks saying “Coming up soon you’ll hear this, {play hook}, some of this {play hook } and some of this {play hook}. Right now, it’s X band with their song X.”

The question is, how do you make this automatic, without having to have an manual intervention everytime? I’m glad you asked. StationPlaylist makes this very easy by using Break Notes to achieve this. To get that exact aforementioned scenario to happen, it’s pretty easy, and I’ll show you the secret.

Firstly, to do this automated, you’ll need a script that is written, and the following audio, put in to their own categories”

  • “Here’s what’s coming up in the next 15 minutes on My Station”
  • “And This great song”, “And this”, “Some of this”
  • “Right now, here’s another great hit on My Station”.

Each of those dot points should have their own category so we can get them playing the right order. It’s also a good idea to have lots of versions of that text, so that this feature doesn’t burn too quickly.

After you have created and added those 3 categories, you’ll need to add the audio. After that, the magiv can happen.

Purely for demonstration purposes, here is a rotation that has a 3 song hook added to it.

As seen, above, the (and for the purposes of this demo, there is a song category called ‘TEST’, which has only 9 songs in it.

The text outlined in red are categories, and match the text categories mentioned above. The “*Hook=X” texts are a break note, that when triggered, will play the Hook X number of songs down the list. So, in this instance, it will only play the hooks of the 3 songs after the song with plays after “.__Here’s another great Hit” category.

Why do it this way? Well, we are creating anticipation, and if a listener knows or hears a favourite song hook, they may just want to stay on longer to listen.

Here’s what this rotation looks like in StationPlaylist before it has been played

You notice that the Hooks have not been decided, or activated yet. Don’t worry, this happens as SPL Studio gets to them They then look like the following:

And the station continues on.

The above playout was not done in automation, hence why there are tick boxes shown.

It’s a simple and effective method for making your station sound great.

As with most hooks, they have reasonably hard markers at the cue points, so it may be worth adding sound effects or a background sound on your audio files.

So far for this scenario, the audio files for categories are saved with fixed crossfades, and are set as “normal” audio files, rather than “Song intro”, “Voice Intro” etc., and it seems to work fine.

Reading and Writing Settings.ini files in Vb.NET

Posted on

Probably the easiest way to read and write settings.ini files in VB.NET is with a little bit of code from NuGET from MarioZ called

This is a section that I wrote using the examples to check and see if values exist, before writing a default settings file.

Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load

    ' Create new file with a default formatting.
    Dim file As New IniFile()

    'The file exists 
    If System.IO.File.Exists("settings.ini") Then
    End If

    Dim section As IniSection = file.Sections.Add("Main")
    section.TrailingComment.Text = "Main Settings"

    Dim Values As String = file.Sections("Main")?.Keys("DataPath")?.Value

    If Values IsNot Nothing Then
        Console.WriteLine("it's not empty" & Values)
        Console.WriteLine("it's empty")

        ' Add new key and its value.
        Dim key As IniKey = section.Keys.Add("DataPath", "c:\temp\test.txt")
        ' Add leading comment.
        key.LeadingComment.Text = "Data path where the schedules live"

        ' Save file.

    End If

    TextBox_Schedule_File.Text = file.Sections("Main")?.Keys("DataPath")?.Value

End Sub

In keeping with the simple example, this writes a value to a file if the file exists.

Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button4.Click
    If (FolderBrowserDialog1.ShowDialog() = DialogResult.OK) Then
        TextBox1.Text = FolderBrowserDialog1.SelectedPath & "\test.txt"

        ' Create new file with a default formatting.
        Dim file As New IniFile()

        'The file exists 
        If System.IO.File.Exists("settings.ini") Then
            file.Sections("Main").Keys("DataPath").Value = TextBox1.Text
        End If
    End If
End Sub

Converting DOS Date/Time in C# stored as HEX

Posted on Updated on

public void Main
    DateTime now = DateTime.Now;
    int yearnowfix = (now.Year + 20);

    string[] theArray = { yearnowfix.ToString().Substring(2),  now.Month.ToString(), now.Day.ToString(), now.Hour.ToString(), now.Minute.ToString(), now.Second.ToString() };  


public string PlayDateConversion_ToHex(String[] HumanDate)
    String DateHEX = "";

    // Convert the array to Binary, to then convert to Hex.
    Console.WriteLine("This is the Binary:");

    Console.WriteLine(ToBinary(Convert.ToInt32(HumanDate[0]), 7)); // Year + 20 (2 digits only)
    Console.WriteLine(ToBinary(Convert.ToInt32(HumanDate[1]), 4)); // Month
    Console.WriteLine(ToBinary(Convert.ToInt32(HumanDate[2]), 5)); // Day

    String DateBin = ToBinary(Convert.ToInt32(HumanDate[0]), 7) + ToBinary(Convert.ToInt32(HumanDate[1]), 4) + ToBinary(Convert.ToInt32(HumanDate[2]), 5);

    Console.WriteLine("DateBin:" + DateBin); 

    Console.WriteLine(ToBinary(Convert.ToInt32(HumanDate[3]), 5)); // Hour
    Console.WriteLine(ToBinary(Convert.ToInt32(HumanDate[4]), 6)); // Minute
    Console.WriteLine(ToBinary(Convert.ToInt32(HumanDate[5]), 5)); // Second

    String TimeBin = ToBinary(Convert.ToInt32(HumanDate[3]), 5) + ToBinary(Convert.ToInt32(HumanDate[4]), 6) + ToBinary(Convert.ToInt32(HumanDate[5]), 5);

    Console.WriteLine("TimeBin:" + TimeBin);

    String HexValue = "";
    String MasterHexValue = "";
    String MixedTimeDateBin = DateBin + TimeBin;
    for (int i = 0; i < 32; i=i+4)

        Console.WriteLine(MixedTimeDateBin.Substring(i, 4)); 
        switch (MixedTimeDateBin.Substring(i, 4))
            case "0000": HexValue = "0"; break;
            case "0001": HexValue = "1"; break;
            case "0010": HexValue = "2"; break;
            case "0011": HexValue = "3"; break;
            case "0100": HexValue = "4"; break;
            case "0101": HexValue = "5"; break;
            case "0110": HexValue = "6"; break;
            case "0111": HexValue = "7"; break;
            case "1000": HexValue = "8"; break;
            case "1001": HexValue = "9"; break;
            case "1010": HexValue = "A"; break;
            case "1011": HexValue = "B"; break;
            case "1100": HexValue = "C"; break;
            case "1101": HexValue = "D"; break;
            case "1110": HexValue = "E"; break;
            case "1111": HexValue = "F"; break;
                return "Invalid number";
        MasterHexValue = MasterHexValue + HexValue;

    Console.WriteLine("HexDate: " + MasterHexValue); 

    String MasterHexValueArrange = "";
    MasterHexValueArrange = MasterHexValue.Substring(6, 2) + MasterHexValue.Substring(4, 2) + MasterHexValue.Substring(2, 2) + MasterHexValue.Substring(0, 2);

    Console.WriteLine("Remixed HexDate: " + MasterHexValueArrange);  
    return MasterHexValueArrange;
public static string ToBinary(Int64 Decimal, int leadingzeros)
    // Declare a few variables we're going to need
    Int64 BinaryHolder;
    char[] BinaryArray;
    string BinaryResult = "";

    while (Decimal > 0)
        BinaryHolder = Decimal % 2;
        BinaryResult += BinaryHolder;
        Decimal = Decimal / 2;

    BinaryArray = BinaryResult.ToCharArray();
    BinaryResult = new string(BinaryArray);

    return BinaryResult.ToString().PadLeft(leadingzeros, '0');

This proved to be a bit of fun, but interesting all the same. Basically, it allows you to store the date and time of YYYYMMDDHHMMSS in to 4 Characters, stored as Hex values.

I did the maths for this to happen on the above code. This storage method of the date and time is probably one of the most complicated ways of storing the date and time due to the conversion from Decimal to Binary and then eventually to HEX Base-15, however it’s also the most character space saving method I’ve seen as well. I didn’t write this method, but some boffins did. I just made it so you can get the “now” time at the end of the routine.

This very efficient date storage is also used in StationPlaylist APE XLastPlayed metadata tag.

I’ve left all the description and workings out in the code so others can learn. No doubt it can be cleaned up further.

You can test these things using HxD hex editor and hoving over 2 characters which will give you a valid date or time under it’s Data Inspector tool.

The original maths came from here:

The following has just been copied from the page.

DOS FileTime

The DOS TimeStamp can be viewed in either Date/Time or Time/Date order. The method for calculating both is the same however.

Example : The current time in DOS (Date/Time)TimeStamp is 9651F706.
Example : The current time in DOS (Time/Date) TimeStamp is F7069651.

Epoch DateN/A

Start by splitting the byte up into two nibbles. These represent the Date and Time (Or Time and Date).

In this example I will use the Date/Time format.


Next, the nibbles need to be switched around to read in the opposite endian.


Now convert the values into Binary


The Binary is then broken up into sections.

Note that only 5 bits are available to record the seconds. That would usually mean a maximum number of seconds of 31 (16 + 8 + 4 + 2 + 1). We obviously need more than that but there aren’t enough bits available. The decision was made that accuracy to 2 seconds would be enough. That meant that you can assume that the least significant bit is always 0. So by adding a 0 to the right side, you end up with 6 bits.

Bit 1Bit 2Bit 3Bit 4Bit 5Bit 6Max Value
Inserting the bit in red at the end shifts all bits to the left

Each Binary value is converted back into Decimal:

BitsValue DescriptionBitsValue Description

So you have the 16th January 40AD at 15:30:10… Oh wait… You may have realised that the year is a little off.

When this timestamp was created, they realized that seconds, minutes, hours, days and months are all finite, with top values of 60, 60, 24, 31 and 12 respectively. Years however presents an issue as they go on (hopefully) almost infinitely.

To combat this, the developers decided that 1980 would be a good place to start and so the YEAR value is added to 1980 in every case. In the example above, the year value is 40. Therefore, 1980 + 40 = 2020.

So the final date is 16th January 2020 at 15:30:10.

Note that these are always LOCAL TIME.

Convert your own DOS Date/Time TimeStamp here :  = 

Convert your own DOS Time/Date TimeStamp here :  = 

Moving from Audition to Studio One – A Radio perspective

Posted on Updated on

I’m doing this on a journey and writing things as I go, because while all audio editors do things the same – play audio – the way you navigate and actually achieve those results is different.

This is not comprehensive, but written as a bit of a starter guide, not only for other people but myself as I remember and learn this software. It have done this from the perspective of Studio One Artist Edition version 4.

At first, the journey is frustrating, but most things can be done. S1 lacks a Spectral View for really looking properly at what is wrong with your audio to be able to EQ out or remove those specific bad frequencies, or heal any audio that’s not quite right. It also does not have a noise remover, like Audition has.

In short, Audition is a great tool if your audio needs restoration and fixing, whereas S1 is best if you start with pristine audio. The two programs are a different beast, but both workable. A bit like using Pro-tools for radio broadcast.

As a side note, if you are planning on transitioning or using both, some translation software I would recommend is the AATranslator tool. This is an Australian company who can translate between different DAWs and audio tools.

It claims to be able to translate the following:

Presonus.captureImport from & Export toImport & export format for PreSonus Studio One and Studio Live/Capture.
Presonus.songImport from & Export toImport & export format for PreSonus Studio One.

On to the next steps.

The biggest thing to know is that Audition is a destructive editor, and Studio One is a non-destructive editor. This means that as in Audition, when you delete a section of audio in the single window editor, that the audio is now gone. S1 on the other hand actually keeps all of your files and is basically the equivalent to the multitrack editor in Audition.

There are some big differences and I’ll try to out line the basic ones that I’ve noticed so far.

Mouse Navigation:

When moving audio in multi-track in Audition, it’s always right-click and drag the file around the session. In S1, it’s actually left-click and drag.

Audition for MultitrackS1
Zooming IN & OUT for TimeCOMMAND+Mouse ScrollSHIFT+COMMAND+Mouse Scroll
Pan Up & DownMouse Scroll Mouse Scroll
Zooming IN & OUT for Track HeightOption+Mouse ScrollCOMMAND+Mouse Scroll

Basic Short Cuts:

There’s many things to figure out and firstly, the keyboard short cuts are going to be your friend, and having to remap your fingers to learn new shortcuts.

Audition for MultitrackStudio One
Move ToolV1
Split a trackCTL-K3
MarkersF8 / MY

Pan and Volume Point Controls

This took me a while to find, but you you click on the little icon under the track name, and you can use it the same way as Audition. The only difference is the lines are not overlayed on the actual waveform.

Moving the Playhead to where the cursor is

I would strongly advise this is selected. You can find this setting in the Preferences menu.

A good thing to know as well, that if you have the bracket icon ‘[‘ selected, they the cursor will behave differently based on where you have it on the track. By default, the top half will be track will be a selection tool, and the bottom will be for moving. Rather intuitive, but very different to Audition.

Turn snapping off for the moment, as it’ll just hurt your head.


If you double click on a track/stem in the main multitrack/song window it will open that track in the Edit section, and will reflect those changes made in the edit window on the main song window. This is not the sames as Audition. It’s more like Abelton Live, where it just shows you a different window of your audio file, except it’s basically the whole track isolated… if that makes sense.

If you come from a video editing background as well, there is ripple editing if you select this icon.


Make yourself a new template as well to make life easier. This is my default one.

But firstly, just choose the Vocal Recording. It should be enough to get you started with all the settings correctly for playback and recording.

If not, you’ll need to set up your devices.

Please note the Timebase and the resolution. 32 Bit Float will give you unlimited headroom, and having the Timebase in seconds will be better than working in beats, bars, time signatures and frames.

The good news is that 10 minutes that is listed, is not for


So, once you have your rough clip of audio sounding nice, it’s time to fix it. S1 is similar to Audition where you can add effects to the audio bus mix / track and it comes out as expected.

The thing is you can’t just select audio and then apply the effect. Remember, this is non destructive editing and so adding an effect does this for the entire track, not for a selection on audio.

These two articles are a good start:

And, this YouTube clip

However, to find these effects, isn’t is as straight forward at first.

Firstly, click on Browse, then Effects, the enter your search keyword. I just typed dynamic and it gave me what I wanted. Usually when I edit, I look for Dynamic Process and also a Hard Limiter. Choose the Compressor and drag in on the the track you which it to apply to.

Then, choose the Mix View, and you’ll see the Compressor show as an Insert on that track, where you can then apply your desired settings.


This is the next biggest pain to learn, and it’s not quite as straight forward, where you just select your work area, and hit CTL-SHIFT-DOWN to create a mixdown.

It always wants to save it to the session location of some sort… You have to make sure it’s all sorted to your file format.

Then, when you think you’ve done it… You get a warning like this. Go figure?

And then…. You get this? But… I just exported it and it took be 10mins?? What???

You should probably then go back and adjust your volume/limiter/compressor settings etc to make the track a little softer. Strangely enough, this also happens using a WAVE file export when using the 32 bit depth. So while there is no distortion in that bit rate, it still warns you.

Even more frustrating with the export window is that while you made your selection to export, and chose “Between Selected Markers”… that’s actually not the case. And choosing “Between Song Start Song/End Marker” will also give you a 10 minute file.

Instead, what I do is select all the audio parts on the track I want as a new mixdown, then right click on one of the tracks and choose “Mixdown Selection”. It will then create a new mixdown on a new track

From there you can Choose “Export Selection” and it will prompt you to save a Wave file in the location of your choice.

Of course, this is just me having found ways to do what I need it to do and there’s probably better ways, but as a start, this is where I’m at right now.

Weird Stuff

Sometimes your track/event becomes mute. This will mean that choosing “Mixdown Selection” will actually mixdown nothing and you’ll have an empty bit of audio. Not sure why this is and it’s very unhelpful. If it happens, right click on it, and choose “Unmute Events” and try your mixdown again if need be.

Also, as a side note, I also use LoopBack to route audio around the Mac. It’s amazing. I have used SoundFlower in the past, but Loopback smashes the pants off it. It’s worth spending the small amount of cash on it.

It just doesn’t sound right?

Sometimes you may be a weird echo and this could be because you are monitoring your sound card playback through S1. If this happens, unclick the little blue box with the white speaker and it should sound clean again.

Apple Silicon for Devs M1?

Posted on

I don’t have any of the new Apple M1 silicon devices. This is on the cards, but first, I have to investigate this.

So, I write this in the unknown, as not many are talking about how this will be for developers.

It’s Just YouTubers doing video benchmarking. which will suite their workflows.

Firstly, Ionic/Angular and Node.

There are many questions, and answers are slowly creeping in.

The questions about running Homebrew…. Can be done? Yes… But, not everything is working as yet. But, see more here.

The other question remains about Ionic. Apparently it works fine for dev work and the localhost server for testing, but “A B” said in the comments said it didn’t build.

This is a pretty big thing, and a show stopping if it’s the case. So we shall see.

Apparently Docker has experimental facilities to run as well, but it’s only experimental, so do be aware of that.

There is also very limited virtualisation available too. So far, no VirtualBox.
Read more here on this stuff though.

Anyway, these are some initial thoughts. Still waiting for more information on all this.

Be careful of rogue FireFox Extensions

Posted on

Today I was working on a website application and I saw a strange thing happen with a date checking related library called moment.js where it was calling a website to get some usage statistics. Well, that’s what I thought was the case initially…

I have a few ad blockers installed in my browser, and so that was a good indication something wasn’t right, especially that traffic was flowing to when it was a locally hosted website.

This got me looking and digging through my own code a lot, until I loaded the same page in Chrome.

Strangely enough, it wasn’t there…

This was a good thing.

Eventually I was able to load the JS in a debug in Firefox and discover it was actually a FF Addon called “YouTube Downloader” version 4.5 to be exact.

Oddly enough there was no homepage associated with it as well.

I tried disabling it and re-enabling it and the problem went and returned. Bingo.

I haven’t been able to find it again since, and in my attempt to disable it, I actually ended up removing it.


This rogue Add-on posts content like the following:



And also other stuff like this too


Anyway, I just thought I’d post it as a warning and in case someone else has this problem.

ScottPlot is amazing – How to get settings.

Posted on Updated on

I’ve recently found ScottPlot, and it’s amazing.

Most things are straight forward, and there’s plenty of recipes to try out to get the graphs to do what you want.

However, sometimes things aren’t as easy as you’d think.

No, I may or may not have gone about this the right or original way as the author intended, but, this will work.

If you have multiple plots on your graph and you need to know which is which, or index order and you know what the label is, then this should help you out.

formsPlot1.plt.PlotVLine(label: "green", lineWidth: 3, x: position, draggable: true, dragLimitLower: 0, dragLimitUpper: Global.samples[filename_value]);
String plotname = "";

int plottablescount = formsPlot1.plt.GetSettings(false).plottables.Count; 
for (int i = 0; i < plottablescount; i++)
Console.WriteLine(formsPlot1.plt.GetSettings(false).plottables[i].ToString());      //  PlottableVLine (green) at X=516587
Console.WriteLine(formsPlot1.plt.GetSettings(false).plottables[i].GetLegendItems());//  ScottPlot.Config.LegendItem[]
Console.WriteLine(formsPlot1.plt.GetSettings(false).plottables[i].GetLimits());     //  x1=516587.000, x2=516587.000, y1=NaN, y2=NaN
Console.WriteLine(formsPlot1.plt.GetSettings(false).plottables[i].GetType());       //  ScottPlot.PlottableVLine
Console.WriteLine(formsPlot1.plt.GetSettings(false).plottables[i].GetHashCode());   //  2383799
var stringlabel = formsPlot1.plt.GetSettings(false).plottables[i].ToString();       //  PlottableVLine (green) at X=516587
Console.WriteLine(Regex.Match(stringlabel, @"\(([^)]*)\)").Groups[1].Value); // This will extract the name of the moveable plotline

plotname = Regex.Match(stringlabel, @"\(([^)]*)\)").Groups[1].Value;

if (plotname == "green")
    // do stuff


It loops through the plots you have and then searches for the label you have given your plot, in my case, the label is called green.

Link to the gist

PS. I don’t know how anyone can use this new WordPress content editor. It’s just awful.

Locking yourself out of your server – again

Posted on

Fortunately this is not something I do very often, but when I do, it’s usually good…

One of the things I like to do is have a document with lots of settings and commands I call my cheat sheet – for times that things don’t go to plan.

This time, I was trying to upload some files and I’d set up my server in such a way that I couldn’t upload files to the correct area, but when I did get them in, I had to manually copy them to the correct location afterwards – kind of a different isolating account, a little for extra security. However, as I hadn’t made any adjustments, I basically forgot how I was doing things, and so in the process, I thought that maybe I hadn’t allowed my home IP for SSH.

Well, that was my big mistake. I used Webmin to add the rule of my home IP. It cleared out everything and essentially I was locked out.

I was able to get back in through the server console, but I spent the next few hours trouble shooting what went wrong.

As mentioned, it’s always good to have these thing written down.

I could connect to the server via SSH, however, I was being rejected on connection.

Trying to connnect with this: ssh -p PORT -vvv USER@x.x.x.x

I would get the following, showing where it would suddenly close the connection.

debug3: send packet: type 50
debug2: we sent a password packet, wait for reply
debug3: receive packet: type 52
debug1: Authentication succeeded (password).
Authenticated to x.x.x.x ([x.x.x.x]:port).
debug1: channel 0: new [client-session]
debug3: ssh_session2_open: channel_new: 0
debug2: channel 0: send open
debug3: send packet: type 90
debug1: Requesting
debug3: send packet: type 80
debug1: Entering interactive session.
debug1: pledge: network
debug3: send packet: type 1
debug1: channel 0: free: client-session, nchannels 1
debug3: channel 0: status: The following connections are open:
#0 client-session (t3 nr0 i0/0 o0/0 e[write]/0 fd 4/5/6 sock -1 cc -1)
debug3: fd 1 is not O_NONBLOCK
Connection to x.x.x.x closed by remote host.
Connection to x.x.x.x closed.
Transferred: sent 2156, received 1788 bytes, in 0.0 seconds
Bytes per second: sent 62256.4, received 51630.0
debug1: Exit status -1 

So, what had happened?

Apart from ‘fixing’ the firewall rules, I had also given myself extra group permissions, and in this case – ‘Apache’. The Apache group seemed to make sense so I could eliminate that extra step of copying, however, in doing so, this is where my problem was.

The Apache account is not allowed to log in, and so my actual login account was inheriting that account’s credentials, and thus, being booted out immediately.

Anyway, I thought I’d let you know.

Adding more permissions don’t always give you more access.

Text to Speech WAV

Posted on Updated on

To make this work properly, you need to make sure you have the right speech pack installed:

The other thing, is to make sure the voices you want to use are enabled correctly.

On Windows 10 – and I have not tested this on anything else, so use it at your own risk!

You will need to run a PowerShell Script, as outlined here:

$sourcePath = 'HKLM:\software\Microsoft\Speech_OneCore\Voices\Tokens' #Where the OneCore voices live
$destinationPath = 'HKLM:\SOFTWARE\Microsoft\Speech\Voices\Tokens' #For 64-bit apps
$destinationPath2 = 'HKLM:\SOFTWARE\WOW6432Node\Microsoft\SPEECH\Voices\Tokens' #For 32-bit apps
cd $destinationPath
$listVoices = Get-ChildItem $sourcePath
foreach($voice in $listVoices)
$source = $voice.PSPath #Get the path of this voices key
copy -Path $source -Destination $destinationPath -Recurse
copy -Path $source -Destination $destinationPath2 -Recurse

Do NOT run this script more than once, because you will get double entries, and who knows if it will break things.

Basically, what it will do is read the voices you have listed as installed, and then copy some registry values to enable them in third party programs, as explained here:

If you don’t understand Power Shell, or Regedit, then don’t be running these scripts.


Sometimes, you may get security problems, so elevating the script may also be required.

This is what happens when the aforementioned happens.

However, when it does run, our registry entries look like this originally:

Then, they look like this:


Next steps are getting a program called Balabolka.

We are going to need the console version for this task.


If you don’t have the correct voice fix installed, you will not be able to use the correct voice you want.

I wanted to have James, but I couldn’t.


But after those fixes, James worked.

I’d also suggest that if you want to script this, it may be easier to use a config file.

-f c:\balabolka\script.txt
-w c:\balabolka\script.wav 
-n James
-s -1
-p -1
-v 95
-e 300
-d d:\Dict\rules.bxd

So, when you now run balcon.exe, you get a wave file with the text file the script.


You can listen to the weather script here without any fixes. This is the Microsoft James voice.

After fixing the paragraphs and changing the voice, this is the Microsoft Catherine voice.


It doesn’t sound great at first, but it’s workable. You can also hear where the end of the line is. This can probably fixed with automation, but it’s a bit of fun.

This is where the script came from:

If will not be the same as this one below, as it changes regularly, but as an example, you get the idea.

As a trial run, this is the script:

Australian Government Bureau of Meteorology
Northern Territory

Northern Territory Forecast
Issued at 5:00 am CST on Wednesday 17 June 2020
for the period until midnight CST Saturday 20 June 2020.

Warning Information
For latest warnings go to, subscribe to RSS feeds, call 1300 659
210* or listen for warnings on relevant TV and radio broadcasts.

Weather Situation
A high pressure system over southern Australia is moving east as the next
trough approaches from WA. While the easterly flow will persist over the Top
End for the next few days, winds in the south will tend northerly, resulting in
temperatures rising again in the south. The change will move through late on
Friday, dropping temperatures in the south to around average for this time of

Forecast for the rest of Wednesday 17 June
A slight chance of showers in the far northeast Arnhem District. Sunny
elsewhere. Light to moderate east to southeast winds, fresh at times during the
morning and early afternoon, particularly south of Elliot.

Fire Danger: 
High to Very High in the Tiwi, Daly, Gregory, Carpentaria and western Arnhem

Forecast for Thursday 18 June
A slight chance of morning showers in the far northeast Arnhem District. Sunny
elsewhere. Areas of morning frost across the Simpson District. A warmer day in
the south. Light to moderate east to southeast winds, tending northeast to
northwest in the southern Tanami, western Simpson and Lasseter Districts during
the afternoon.

Forecast for Friday 19 June
A slight chance of showers in the far eastern Arnhem District. Sunny elsewhere
before some cloud develops along the eastern border during the evening. Warmer
in the south. Light to moderate east to southeast winds, tending northeast to
northwest south of Barrow Creek ahead of a south to southwest change developing
over the Lasseter District later in the day.

Forecast for Saturday 20 June
A slight to medium chance of showers in the far eastern Arnhem District. Sunny
elsewhere after early morning cloud along the eastern border. Cooler south of
Barrow Creek. Light to moderate east to southeast winds, fresh and gusty during
the morning in the Lasseter and Simpson Districts.

The next routine forecast will be issued at 4:30 pm CST Wednesday.

* Calls to 1300 numbers cost around 27.5c inc GST, higher from mobiles or
public phones.

Copyright Commonwealth of Australia 2011, Bureau of Meteorology (ABN 92 637 533
532). Users of these web pages are deemed to have read and accepted the
conditions described in the Copyright, Disclaimer, and Privacy statements

As a side note, there is also Amazon Polly and Google’s Cloud Text-to-Speech.