πŸ“œ ⬆️ ⬇️

Again about Electron or drawing music VK

image

Good for all!
Electron - this is such a funny thing, about which there are few articles on HabrΓ© (immediately found only habrahabr.ru/post/272075 and habrahabr.ru/post/278951 ). I have long wanted to write something like that, so my hands have reached - and at the same time one bike in the world will be bigger.

So, briefly: electron is such a hybrid of node.js and chromium. What for? A very diverse range of applications - powerful GUI (html / js / css), non-generous extensibility (including the ability to use other languages ​​like C ++ or C #), all sorts of amenities like jQuery, etc. In general, a handy thing for the development and distribution of standalone cross-platform applications.
Now about the application. It implements the basic example of expanding the functionality of a third-party site, the basic principles of working with Raphael.js (graphic library for drawing / animation svg), Dancer.js (library for visualizing sound, in this case - receiving audio waveform).
')


Let's start with the project structure.

image

Package.json is a project description with an entry point (in our case, index.js). The rest of the files I usually put in the views folder - so called out of habit, rather than with a desire to indicate its purpose.

Now consider the entry point. At the moment, I came across 3 approaches on the topic of cooperation between the nod context and page content: preload script + normal page navigation, iframe or webview.
Webview, frankly, I don’t like it due to the inability to directly manipulate content from the parent window (you need to either use IPC or global objects via remote.getGlobal (transmitted, which is characteristic, not a link to the object, but, frankly, a fucking clone For example, manipulating the contents of the window will not work.) However, it also has all sorts of goodies like the substitution of referer, useragent, preload-scripts and other stuff, dryuchek. Great if you do not need to work with the content of the page and
Iframe is already better - there is direct access to the content (even the crossdomain, if you cut down web-security), but sometimes there are deception in the style of checking window.top. Do you think there is something like nwfaketop from node-webkit here? Oh, if only. I, unfortunately, did not find a normal way. Great for most cases in which there is no check window.top and other pleasures of life. Unfortunately, vk have them.
The third way is, in general, perhaps the easiest, although it has its limitations - for example, scripts in pages that have a check for module / define / exports (such as jQuery or Raphael) die happily at the sight of nodovskih pieces, and therefore you have to cut down node-integration and use the node context only in preload scripts. However, the security of this also requires, so not too hard and upset.

Index.js is mostly standard for me from the docks, with a couple of exceptions:

"use strict";
(function () {
  var app = require('app');
  var BrowserWindow = require('browser-window');
  
  app.commandLine.appendSwitch('disable-web-security');
  app.commandLine.appendSwitch('web-security');
  app.commandLine.appendSwitch('allow-displaying-insecure-content');
  app.commandLine.appendSwitch('ignore-certificate-errors');

  var mainWindow = null;
  app.on('window-all-closed', function() {
    if (process.platform != 'darwin')
      app.quit();
  });

  app.on('ready', function() {
    mainWindow = new BrowserWindow({
        width: 800, 
        height: 600,
        'web-preferences': {
            'web-security': false
        },
        'node-integration': false,
        preload: __dirname + '/views/index.js'
    });
    
    
    mainWindow.webContents.session.webRequest.onBeforeRequest({}, (d,c)=>{
       if(d.url.indexOf('http://m.vk.com/js/s_c.js')==0){
            var localFile='file://'+__dirname+"/views/jsadditive/s_c.js";
            console.log(localFile);
            c({redirectURL: localFile});
       }
       else 
            c({cancel: false}) 
    });
    
    mainWindow.loadUrl('http://m.vk.com/audios1?performer=1&q=Tonight%20alive');
    
    mainWindow.toggleDevTools();
    mainWindow.on('closed', function() {
        mainWindow.removeAllListeners();   
        mainWindow = null;
    });
  });
}) ();


β€” appendSwitch. chromium'. 2 , , web-security( , - ). 3 http https , 4 β€” ( , , , , , ). , 2 4 .
β€” node-integration:false preload-. , preload . web-preferences, web-security . β€” .
β€” onBeforeRequest. / , 1 β€” dancer.js audio- , , .
loadUrl vk , , … .

views/index.js. .

var fs=require('fs');
var include=(path)=>{
    var exports = undefined;
    (1,eval)(fs.readFileSync(__dirname+path, 'UTF-8'));
};
var link=(path)=>{
    var elem=document.createElement('link');
    elem.setAttribute('rel', 'stylesheet');
    elem.setAttribute('href', 'file://'+__dirname+path);
    document.head.appendChild(elem);
};

window.addEventListener('load', ()=>{
    link('/css/index.css');
    setTimeout(()=>{        
        include('/js/jquery-2.2.3.min.js');
        include('/js/raphael.js');
        include('/js/dancer.min.js');
        include('/js/main.js');
    }, 100);
});


js'. include , window, , eval , . (1,eval) , β€” - . , jQuery exports.
link css'.
, / , .

, , . β€” , .

jQuery('<div>').attr('id', 'output').insertBefore('#au_search_items');
var p=document.querySelector('#output');
var {offsetWidth: w, offsetHeight: h}=p;
var prev;
var time=10;
var mid=h/2;  
var step=10;
var chunks=w/step;
var baseArr=[];
var median=0;
var prevtime=0;
var r = Raphael("output", w, h);



var genPath = (x,isClosing=true)=>{
    var t=[];
    x.forEach((y)=>t.push(...[...y, ' ']));  
    return t.join(' ')+(isClosing?"z":"");
};

var anim, pathq;
var genRand=()=>{  
    var arr=[
        ['M', 0, mid]
    ];
    var baseW = 0;   
    var i=0;
    while(baseW<w)
        arr.push(['L', baseW+=step, baseArr[i++]]);       
    arr.push(['L', w, mid]);
    arr.push(['L', w, h]);
    arr.push(['L', 0, h]);
    
    pathq=genPath(arr, false);
    delete arr;
    
    if(!prev){
        prev=r.path(pathq).attr({
            //stroke: 'grey', 
            fill: 'grey'
        });
    }  
    
    /*if(!anim)
        anim = Raphael.animation({path: path}, time, "<>");
    anim.anim[Object.keys(anim.anim)[0]].path=path;*/
    //prev.animate(anim);
    prev.attr('path', pathq);
};



var dancer = new Dancer();
window.dancer=dancer;
dancer.bind('update', function(){
    var d=Date.now();
    if(d-prevtime>time)
        prevtime=d;
    else
        return;
        
    baseArr=[];     
    var waveForm=Array.from(this.getWaveform());
    var chunkLength=waveForm.length/chunks;
    while(waveForm.length>0)
        baseArr.push((waveForm.splice(0, chunkLength).reduce((a,b)=>a+b)/chunkLength)/((dancer.audio&&(dancer.audio.volume>0))?dancer.audio.volume:1)*h/2+h/2);    
    requestAnimationFrame(genRand);
});


.
1 , ' . , .
var {offsetWidth: w, offsetHeight: h}=p; β€” ES6. Β« . - . - var w=p.offsetWidth. ES6 β€” , - . β€” ( , ), .

- genPath. ( )( , this , β€” arguments), (isClosing=true) spread operator( . , [1,2,3].push(...[4,5]) ->[1,2,3,4,5]. , , . ).
, svg- . .

genRand genRand, genSvgFromThoseAudioWaveformWhateverIsIt. , nobody cares.
, , svg path audio waveforms, baseArr. , stroke (, fill , - ). , cpu( ~20% ~10%).
, Raphael.js(. ).

, , dancer.
, Web audio API. , ( , ).

Callback update , this.getWaveform() 0 1, .
. β€” requestAnimationFrame. . β€” .
β€” baseArr.push. . reduce, , , , .

-, .
β€” github.com/demogoran/vkvisual.
electron β€” github.com/electron/electron?utm_content=buffer703cb&utm_medium=social&utm_source=twitter.com&utm_campaign=buffer.
β€” github.com/electron-userland/electron-prebuilt.

β€” npm install -g electron-prebuilt + electron. .

, !

Source: https://habr.com/ru/post/281278/


All Articles