How to listen to the radio using powershell and node.js

    Part one: powershell and mci .

    The operating system used is Windows 7.
    The first question that arises is: how to play mp3 in powershell?
    Earlier on a hub there were mentions of a similar question:
    How to play mp3 from the command line in Windows 7?
    How to use PowerShell?

    Due to the lack of a satisfactory answer, it was decided to use the Media Control Interface or mci.
    The advantages are the following: built-in system, sufficient low level, it is possible to send commands as a string.
    Many mci are known for answering the question: “how to open a CD-ROM drive programmatically?” Although it provides many other features. Below are some usage scenarios.

    Removing a CD-ROM:
    mci 'set cdaudio door open'
    

    Play local mp3 file:
    mci 'play C:\\temp\\Kalimba.mp3'
    mci 'status C:\\temp\\Kalimba.mp3 mode'
    

    Write to wav file:
    mci 'open new type waveaudio alias RecWavFile'
    mci 'set RecWavFile bitspersample 16 samplespersec 44100 channels 2'
    mci 'record RecWavFile'
    mci 'stop RecWavFile'
    mci 'save RecWavFile C:\\temp\\RecWavFile.wav'
    mci 'close RecWavFile'
    mci 'play C:\\temp\\RecWavFile.wav wait'
    

    A simple way to play a stream from a network:
    mci 'play http://some-radio-server.com:80/some-radio-channel.mp3'
    


    Advanced option for playing a stream from the network:
    mci 'open http://some-radio-server.com:80/some-radio-channel.mp3 type mpegvideo alias radio'
    #mci 'open http://other-radio-server/other-radio-channel.fake.mp3 type mpegvideo alias radio'
    mci 'status radio volume'
    mci 'setaudio radio volume to 100'
    mci 'play radio'
    mci 'status radio mode'
    mci 'stop radio'
    mci 'close radio'
    


    It may seem strange, but initially in powershell there is no such way (the author did not find) working with mci. To make this work possible, you need to write a wrapper on the mciSendStringA function. At the same time, in order to have a full-fledged command line interface, in addition to entering commands, it is necessary to output the result of the function execution and decrypt error codes. The first is necessary, for example, to request status with the status command. The second is convenient, since you do not need to search for a description of the error each time.

    mci powershell wrapper:
    $MemDef =@"
    [DllImport("winmm.dll", CharSet = CharSet.Ansi)]
    public static extern int mciSendStringA(
    string lpstrCommand,
    [Out] byte[] lpstrReturnString,
    int uReturnLength,
    IntPtr hwndCallback);
    [DllImport("winmm.dll", CharSet = CharSet.Ansi)]
    public static extern int mciGetErrorStringA(
    int dwError,
    [Out] byte[] lpstrBuffer,
    int uLength);
    "@
    $winmm = Add-Type -memberDefinition $MemDef -ErrorAction 'SilentlyContinue' -passthru -name mciSendString
    function mci($strSend)
    {
        $buffer = New-Object byte[] 128
        [int]$error = $winmm::mciSendStringA( $strSend, $buffer, 128, 0 )
        [Text.Encoding]::ASCII.GetString($buffer)
        $error_buffer = New-Object byte[] 128
        [void]$winmm::mciGetErrorStringA( $error, $error_buffer, 128 )
        [Text.Encoding]::GetEncoding('windows-1251').GetString($error_buffer)
    }
    

    Some clarifications may be helpful.
    $ MemDef contains a piece of C # code. And inside $ winmm is already the result of compiling it.
    To get a text string, you need from the library function:
    1. declare the type of the returned string (in C # code, which means the register is relevant) as [Out] byte [];
    2. when calling, prepare a buffer;
    3. pass buffer and buffer size;
    4. interpret received bytes as a string in a specific encoding.

    It may seem that using C # inclusions, especially to declare winapi functions, is not very correct. But here it is usability that is important, and not the way to achieve it: the line with the mci command harmoniously blends into the powershell style.

    A list of mci commands can be found here or by searching for “Multimedia Command Strings”.
    PS Through mci, you can watch movies.

    Part two: http proxy and node.js

    As you can see from the examples: it is possible to specify not only local files, but also URLs as a name.
    But for some servers (such as other-radio-server) there is a problem: playback starts only after a forced disconnection. It can help your own HTTP proxy "correcting" the "wrong" packets. In particular, for immediate playback, just add the header 'content-length' to the response.
    Node.js is best for writing your proxy.

    After starting and connecting the proxy, you can use the commented out line instead of the one preceding it.
    The end of the url ".fake.mp3" indicates the need for proxy manipulation. Below is the server code that is far from optimal.

    node / proxy_server.js:
    require('./simple_cli').run()
    var http = require('http')
    var url = require('url')
    http.createServer(function(request, response) {
      var u = url.parse(request.url)
      var options = {
        port     : u['port'] || 80,
        host     : u['hostname'],
        method   : request.method,
        path     : u['pathname'],
        headers  : request.headers
      }
      console.log(request.url)
      delete options.headers['proxy-connection']
      if( request.url.substr(-9) === ".fake.mp3" )
      {
        var p = options['path']
        options['path'] = p.substr(0, p.length - 9)
        options['icy-metadata'] = '0'
      }
      console.log(options)
      var proxy_request = http.request(options);
      proxy_request.on('response', function (proxy_response) {
        console.log(proxy_response.headers)
        if( request.url.substr(-9) === ".fake.mp3" )
        {
          proxy_response.headers['content-length'] = '221183499'
        }
        proxy_response.pipe(response);
        response.writeHead(proxy_response.statusCode, proxy_response.headers);
      });
      request.pipe(proxy_request);
    }).listen(8080);
    console.log('Server running...');
    


    PS A proxy can also be useful, for example, for viewing or preprocessing sent requests and received responses.

    Part Three: Interactivity

    Sometimes, for the convenience of viewing the headers passing through the server, you need to clear the console. The simplest command line interface allows this to be achieved.
    node / simple_cli.js
    module.exports.run = function(){
      process.stdin.resume();
      process.stdin.setEncoding('utf8');
      process.stdin.on('data', function (data) {
        data = data.toString().trim();
        switch (data)
        {
        case 'exit':
          process.exit();
          break;
        case 'clear':
          process.stdout.write('\u001B[2J\u001B[0;0f');
          break;
        case 'help':
          process.stdout.write('exit, clear, help\n');
          break;
        default:
          process.stdout.write('unrecognize: '+data+'\n');
        }
      });
    }
    

    This is necessary for convenient server startup:
    run_proxy_server.cmd
    "%~d0%~p0node.exe" "%~d0%~p0node\proxy_server.js"
    pause
    


    The final recipe:
    1. download to the working folder node.exe with " nodejs.org/download "
      After creating all the files in it, we get the following tree:
      +
      | -node.exe
      | -run_proxy_server.cmd
      | + node
      | | -proxy_server.js
      | | -simple_cli.js
    2. run http proxy:
      run_proxy_server.cmd
    3. set the local proxy:
      windows + R, control, ctrl + F, pr
      “Configure proxy server”, “Configure network”, “Proxy server”, “Use proxy server ....”
      Address: 127.0.0.1 Port: 8080
      Ok
    4. interactively execute the wrapper code
    5. interactively execute examples

    For the last two points, you can use, for example, any of the options:
    • poweshell from the command line
    • Windows PowerShell
    • Windows PowerShell ISE


    PS For some-radio-server and some-radio-channel you can take, for example, icy-e-05.sharp-stream and kiss100low, respectively, and for other-radio-server and other-radio-channel - pub5.di .fm and di_eurodance.

    Also popular now: