2016-02-20 40 views
7

Próbuję zrobić coś, co moim zdaniem byłoby proste. Używam nwjs (wcześniej nazywanego Node-Webkit), który, jeśli nie wiesz, w zasadzie oznacza, że ​​tworzę aplikację na komputery stacjonarne, używając węzła Chromium &, gdzie DOM jest w tym samym zakresie co węzeł. Chcę odciążyć pracę webworkerowi, aby GUI nie zawiesił się, gdy wyślę trochę tekstu do Ivona Cloud (przy użyciu węzła ivona), który jest API do tekstu na mowę. Dźwięk wraca w porcjach, gdy jest generowany i zapisywany do formatu MP3. ivona-node używa fs do zapisu pliku mp3 na dysk. Mam to działa w dom, ale webworkers są potrzebne, aby nie zawiesić interfejsu użytkownika. Tak więc mam dwa moduły węzłów, które muszę użyć w webworker, ivona-node i fs.Nie można wymagać modułów węzłów w WebWorker (NWJS)

Problem polega na tym, że w przeglądarce sieciowej nie można użyć wymagania. Spróbowałem więc pakować ivona-node i fs z browserify (istnieje pakiet o nazwie browserify-fs do tego, którego użyłem) i zastępuję require z importScripts(). Teraz otrzymuję błędy var w modułach węzła.

Uwaga: Nie sądzę, aby metoda native_fs_ działała na zapisywanie pliku mp3 na dysku w porcjach (The stream), tak jak powinna, i również dostaję błędy w pakiecie Ivona (w rzeczywistości przede wszystkim), że nie wiem jak to naprawić. Załączam wszystkie informacje, aby to odtworzyć.

Oto błąd Dostaję w konsoli: Uncaught SyntaxError: Nieoczekiwany token var ivonabundle.js VM39: 23132

  • Kroki prowadzące do odtworzenia w NWJS:

npm instalują Ivona-node

npm zainstalować browserify-FS

KMP zainstalować -g browserify

  • Teraz browserified main.js dla Ivona-węzeł i index.js dla browserify-FS:

browserify main.js> ivonabundle.js

browserify index.js> fsbundle.js


package.json ...

{ 
    "name": "appname", 
    "description": "appdescr", 
    "title": "apptitle", 
    "main": "index.html", 
    "window": 
    { 
    "toolbar": true, 
    "resizable": false, 
    "width": 800, 
    "height": 500 
    }, 
    "webkit": 
    { 
    "plugin": true 
    } 
} 

index.html ...

<html> 
<head> 
    <title>apptitle</title> 
</head> 
<body> 

<p><output id="result"></output></p> 
<button onclick="startWorker()">Start Worker</button> 
<button onclick="stopWorker()">Stop Worker</button> 
<br><br> 

<script> 
    var w; 

    function startWorker() { 
     if(typeof(Worker) !== "undefined") { 
      if(typeof(w) == "undefined") { 
       w = new Worker("TTMP3.worker.js"); 
       w.postMessage(['This is some text to speak.']); 
      } 
      w.onmessage = function(event) { 
       document.getElementById("result").innerHTML = event.data; 
      }; 
     } else { 
      document.getElementById("result").innerHTML = "Sorry! No Web Worker support."; 
     } 
    } 

    function stopWorker() { 
     w.terminate(); 
     w = undefined; 
    } 
</script> 
</body> 
</html> 

TTMP3.worker.js ...

importScripts('node_modules/browserify-fs/fsbundle.js','node_modules/ivona-node/src/ivonabundle.js'); 
onmessage = function T2MP3(Text2Speak) 
{ 
postMessage(Text2Speak.data[0]); 
//var fs = require('fs'), 

// Ivona = require('ivona-node'); 

var ivona = new Ivona({ 
    accessKey: 'xxxxxxxxxxx', 
    secretKey: 'xxxxxxxxxxx' 
}); 

//ivona.listVoices() 
//.on('end', function(voices) { 
//console.log(voices); 
//}); 

// ivona.createVoice(text, config) 
// [string] text - the text to be spoken 
// [object] config (optional) - override Ivona request via 'body' value 
ivona.createVoice(Text2Speak.data[0], { 
    body: { 
     voice: { 
      name: 'Salli', 
      language: 'en-US', 
      gender: 'Female' 
     } 
    } 
}).pipe(fs.createWriteStream('text.mp3')); 
postMessage("Done"); 
} 
+0

zrobić trzeba zapisać go na dysku? możesz przesłać dane bin od pracownika na górę, aby zapisać, jeśli naprawdę potrzebujesz, ale nie rozumiem, dlaczego w ogóle trzeba go zapisać. – dandavis

+0

Może, ale co z błędami, które dostaję z węzła ivona? Nie dostałem ich w dom, tylko w webworker. – xendi

+0

Uncaught SyntaxError: nieoczekiwany token var VM39 ivonabundle.js: 23132 – xendi

Odpowiedz

3

Są dwie rzeczy, które ja wan wskazać pierwszy:

  1. zawierających moduły węzłów w przeglądarce internetowej:

W celu włączenia modułu ivona-node musiałem zmienić trochę kod ts. Kiedy próbuję go zaimportować, pojawia się błąd: Uncaught Error: Cannot find module '/node_modules/ivona-node/src/proxy'. Sprawdzając wygenerowany bundle.js zauważam, że nie zawiera on kodu modułu proxy, który znajduje się w pliku proxy.js w folderze src z.Mogę załadować moduł proxy zmieniający tę linię HttpsPA = require(__dirname + '/proxy'); przez: HttpsPA = require('./proxy');. Po tym ivona-node można załadować po stronie klienta przez browserify. Potem napotkałem kolejny błąd podczas próby podążania za przykładem. Okaże się, że ten kod:

ivona.createVoice(Text2Speak.data[0], { 
    body: { 
     voice: { 
      name: 'Salli', 
      language: 'en-US', 
      gender: 'Female' 
     } 
    } 
}).pipe(fs.createWriteStream('text.mp3')); 

nie jest poprawna, to powoduje błąd: Uncaught Error: Cannot pipe. Not readable. Problemem tutaj jest w module http. moduł browserify owinął wiele wbudowanych modułów npm, co oznacza, że ​​są one dostępne, gdy używasz require() lub używasz ich funkcjonalności. http jest jednym z nich, ale można tutaj znaleźć odniesienie: strem-http, Próbuje dopasować api i zachowanie węzła do węzła tak blisko, jak to możliwe, ale niektóre funkcje nie są dostępne, ponieważ przeglądarki nie dają prawie takiej samej kontroli nad żądaniami. Bardzo istotny jest fakt, klasy http.ClientRequest ta klasa w nodejs środowiska utworzyć OutgoingMessage które produkują tego oświadczenia Stream.call(this) pozwalającą na stosowanie metody pipe we wniosku, ale w wersji browserify gdy dzwonisz https.request wynik jest Writable Stream, to jest połączenie wewnątrz ClientRequest: stream.Writable.call(self). Więc mamy wyraźnie do WritableStream nawet z tą metodą:

Writable.prototype.pipe = function() { 
    this.emit('error', new Error('Cannot pipe. Not readable.')); 
}; 

odpowiedzialnego powyższego błędu. Teraz musimy użyć innego podejścia, aby zapisać dane z ivona-node, które pozostawiają mnie do drugiego wydania.

  1. Utwórz plik od pracownika internetowej

Dobrze wiadomo, że o dostęp do systemu plików z aplikacji internetowej mają wiele problemów z bezpieczeństwem, więc problem jest to, jak my może mieć dostęp do FileSystem od pracownika sieci. Jednym z pierwszych sposobów jest używanie HTML5 FileSystem API. Takie podejście jest niewygodne, ponieważ działa w piaskownicy, więc jeśli mamy w aplikacji komputerowej, chcemy mieć dostęp do OS FileSystem. Aby osiągnąć ten cel, możemy przekazać dane od pracownika sieci do głównego wątku, w którym możemy wykorzystać wszystkie funkcje FileSystem nodejs. Pracownik WWW udostępnia funkcję o nazwie Transferable Objects, można uzyskać więcej informacji here i here, za pomocą których możemy przekazać dane odebrane z modułu ivona-node w narzędziu webowym do głównego wątku, a następnie użyć require('fs') w taki sam sposób, jak zapewniamy nam node-webkit. Są to krok można wykonać:

  1. zainstalować browserify

    npm install -g browserify 
    
  2. zainstalować ivona-node

    npm install ivona-node --save 
    
  3. iść do node_modules/ivona-node/src/main.js i zmienić linię:

    HttpsPA = require(__dirname + '/proxy');

    przez to:

    HttpsPA = require('./proxy');

  4. utworzyć bundle.js.

    Tutaj masz kilka alternatyw, stworzyć bundle.js aby umożliwić require() lub umieścić jakiś kod w pliku z pewną logiką, co chcesz (można rzeczywiście obejmują cały kod pracownika internetowej), a następnie utworzyć bundle.js. W tym przykładzie będę tworzyć tylko bundle.js dla mieć dostęp do require() i używać importScripts() w aktach pracownika internetowej

    browserify -r ivona-node > ibundle.js

  5. Put razem

    Modyfikacja kodu pracownika internetowej i index.html w celu aby otrzymać dane w przeglądarce internetowej i wysłać ją do głównego wątku (w index.html)

to jest kod strony internetowej rker (MyWorker.js)

importScripts('ibundle.js'); 
var Ivona = require('ivona-node'); 

onmessage = function T2MP3(Text2Speak) 
{ 
    var ivona = new Ivona({ 
     accessKey: 'xxxxxxxxxxxx', 
     secretKey: 'xxxxxxxxxxxx' 
    }); 

    var req = ivona.createVoice(Text2Speak.data[0], { 
     body: { 
      voice: { 
       name: 'Salli', 
       language: 'en-US', 
       gender: 'Female' 
      } 
     } 
    }); 

    req.on('data', function(chunk){ 
     var arr = new Uint8Array(chunk); 
     postMessage({event: 'data', data: arr}, [arr.buffer]); 
    }); 

    req.on('end', function(){ 
     postMessage(Text2Speak.data[0]); 
    }); 

} 

i index.html:

<html> 
<head> 
    <title>apptitle</title> 
</head> 
<body> 

<p><output id="result"></output></p> 
<button onclick="startWorker()">Start Worker</button> 
<button onclick="stopWorker()">Stop Worker</button> 
<br><br> 

<script> 
    var w; 
    var fs = require('fs'); 

    function startWorker() { 
     var writer = fs.createWriteStream('text.mp3'); 
     if(typeof(Worker) !== "undefined") { 
      if(typeof(w) == "undefined") { 
       w = new Worker("MyWorker.js"); 

       w.postMessage(['This is some text to speak.']); 
      } 
      w.onmessage = function(event) { 
       var data = event.data; 
       if(data.event !== undefined && data.event == 'data'){ 
        var buffer = new Buffer(data.data); 
        writer.write(buffer); 
       } 
       else{ 
        writer.end(); 
        document.getElementById("result").innerHTML = data; 
       } 

      }; 
     } else { 
      document.getElementById("result").innerHTML = "Sorry! No Web Worker support."; 
     } 
    } 

    function stopWorker() { 
     w.terminate(); 
     w = undefined; 
    } 
</script> 
</body> 
</html>