Kodowanie Cabin Fever: samouczek dotyczący back-endu Node.js

Opublikowany: 2022-03-11

Blokada COVID-19 sprawiła, że ​​wielu z nas utknęło w domu, być może mając nadzieję, że sama gorączka w kabinie jest najgorszym rodzajem gorączki, jakiej doświadczymy. Wielu z nas konsumuje więcej treści wideo niż kiedykolwiek wcześniej. Chociaż ćwiczenia są teraz szczególnie ważne, czasami pojawia się nostalgia za luksusem dobrego, staromodnego pilota, gdy laptop jest poza zasięgiem.

W tym miejscu wkracza ten projekt: możliwość przekształcenia dowolnego smartfona — nawet starego, bezużytecznego ze względu na brak aktualizacji — w poręczny pilot do następnego Netflix/YouTube/Amazon Prime Video/itd. obserwacja. Jest to również samouczek dotyczący back-endu Node.js: szansa na poznanie podstaw back-endowego JavaScriptu przy użyciu frameworka Express i silnika szablonów Pug (dawniej Jade).

Jeśli brzmi to zniechęcająco, na końcu zostanie zaprezentowany cały projekt Node.js; czytelnicy muszą uczyć się tylko tyle, ile są zainteresowani nauką, a po drodze będzie sporo łagodniejszych wyjaśnień niektórych podstaw, które bardziej doświadczeni czytelnicy mogą pominąć.

Dlaczego nie tylko...?

Czytelnicy mogą się zastanawiać: „Po co zajmować się kodowaniem back-endu Node.js?” (Oczywiście poza możliwością uczenia się.) „Czy nie ma już na to aplikacji?”

Jasne — mnóstwo z nich. Istnieją jednak dwa główne powody, dla których może to nie być pożądane:

  1. Dla tych, którzy próbują zmienić przeznaczenie starszego telefonu, może to po prostu nie być już opcją , tak jak w przypadku urządzenia z systemem Windows Phone 8.1, którego chciałem użyć. (Sklep z aplikacjami został oficjalnie zamknięty pod koniec 2019 r.)
  2. Zaufanie (lub jego brak). Podobnie jak w przypadku wielu aplikacji, które można znaleźć na dowolnej platformie mobilnej, często wymagają one od użytkowników przyznania znacznie większych uprawnień niż aplikacja potrzebuje do tego, co ma robić. Ale nawet jeśli ten aspekt jest odpowiednio ograniczony, natura aplikacji do zdalnego sterowania oznacza, że ​​użytkownicy nadal muszą ufać, że twórcy aplikacji nie nadużywają swoich uprawnień na pulpicie rozwiązania, włączając oprogramowanie szpiegujące lub inne złośliwe oprogramowanie.

Te problemy pojawiły się od dłuższego czasu i były nawet motywacją do podobnego projektu z 2014 roku, który został znaleziony na GitHub. nvm ułatwia instalowanie starszych wersji Node.js, a nawet jeśli kilka zależności wymagało aktualizacji, Node.js cieszył się doskonałą reputacją kompatybilności wstecznej.

Niestety wygrał bitrot. Uparte podejście i kompatybilność z back-endem Node.js nie mogły się równać z niekończącymi się deprecjacjami i niemożliwymi pętlami zależności między starymi wersjami Grunta, Bowera i dziesiątkami innych komponentów. Kilka godzin później stało się jasne, że znacznie łatwiej będzie zacząć od zera — mimo to autor radzi, by nie wymyślać koła na nowo.

Nowe gadżety ze starego: Zmiana przeznaczenia telefonów na zdalne sterowanie za pomocą zaplecza Node.js

Po pierwsze, zauważ, że ten projekt Node.js jest obecnie specyficzny dla Linuksa - opracowany i przetestowany w szczególności na Linux Mint 19 i Linux Mint 19.3 - ale z pewnością można dodać obsługę innych platform. Może już działać na komputerze Mac.

Zakładając, że zainstalowana jest nowoczesna wersja Node.js, a wiersz poleceń jest otwarty w nowym katalogu, który będzie służył jako katalog główny projektu, jesteśmy gotowi do rozpoczęcia pracy z Express:

 npx express-generator --view=pug

Uwaga: tutaj npx to przydatne narzędzie, które jest dostarczane z npm , menedżerem pakietów Node.js dostarczanym z Node.js. Używamy go do uruchomienia generatora szkieletów aplikacji Express. W chwili pisania tego tekstu generator tworzy projekt Express/Node.js, który domyślnie nadal pobiera silnik szablonów o nazwie Jade, mimo że projekt Jade zmienił nazwę na „Pug” od wersji 2.0. Aby więc być na bieżąco i od razu korzystać z Puga — plus unikaj ostrzeżeń o przestarzałości — --view=pug , opcję wiersza polecenia dla skryptu express-generator uruchamianego przez npx .

Gdy to zrobimy, musimy zainstalować kilka pakietów z nowo wypełnionej listy zależności naszego projektu Node.js w package.json . Tradycyjnym sposobem na to jest uruchomienie npm i ( i dla „instalacji”). Ale niektórzy nadal wolą szybkość przędzy, więc jeśli masz ją zainstalowaną, po prostu uruchom yarn bez parametrów.

W takim przypadku należy bezpiecznie zignorować (miejmy nadzieję, że wkrótce zostanie naprawione) ostrzeżenie o wycofaniu z jednej z zależności podrzędnych Pug, o ile dostęp jest utrzymywany w razie potrzeby w sieci lokalnej.

Szybki yarn start lub npm start , po którym następuje przejście do localhost:3000 w przeglądarce, pokazuje, że nasz podstawowy back-end Node.js oparty na Express działa. Możemy go zabić za pomocą Ctrl+C .

Samouczek zaplecza Node.js, krok 2: Jak wysyłać naciśnięcia klawiszy na maszynie hosta

Mając już w połowie zakończoną część zdalną , zwróćmy uwagę na część kontrolną . Potrzebujemy czegoś, co może programowo sterować maszyną, na której uruchomimy nasz back-end Node.js, udając, że naciska klawisze na klawiaturze.

W tym celu zainstalujemy xdotool z jego oficjalnymi instrukcjami. Szybki test ich przykładowego polecenia w terminalu:

 xdotool search "Mozilla Firefox" windowactivate --sync key --clearmodifiers ctrl+l

… powinien zrobić dokładnie to, co mówi, zakładając, że Mozilla Firefox jest w tym czasie otwarta. Dobre! Łatwo jest sprawić, by nasz projekt Node.js wywoływał narzędzia wiersza poleceń, takie jak xdotool , jak wkrótce zobaczymy.

Samouczek dotyczący zaplecza Node.js, krok 3: Projektowanie funkcji

Może to nie dotyczyć wszystkich, ale osobiście uważam, że wiele nowoczesnych fizycznych pilotów ma około pięć razy więcej przycisków, niż kiedykolwiek będę używał. Tak więc w tym projekcie patrzymy na układ pełnoekranowy z siatką trzy na trzy ładnych, dużych, łatwych do namierzenia przycisków. To, jakie mogą być te dziewięć przycisków, zależy od osobistych preferencji.

Okazuje się, że skróty klawiaturowe dla nawet najprostszych funkcji nie są identyczne w serwisach Netflix, YouTube i Amazon Prime Video. Usługi te nie działają również z ogólnymi klawiszami multimedialnymi, jak prawdopodobnie natywna aplikacja odtwarzacza muzyki. Ponadto niektóre funkcje mogą nie być dostępne we wszystkich usługach.

Musimy więc zdefiniować inny układ zdalnego sterowania dla każdej usługi i zapewnić sposób przełączania się między nimi.

Definiowanie układów pilota zdalnego sterowania i mapowanie ich do skrótów klawiaturowych

Zróbmy szybki prototyp pracujący z kilkoma ustawieniami wstępnymi. Umieścimy je w common/preset_commands.js —„common”, ponieważ uwzględnimy te dane z więcej niż jednego pliku:

 module.exports = { // We could use ️ but some older phones (eg, Android 5.1.1) won't show it, hence ️ instead 'Netflix': { commands: { '-': 'Escape', '+': 'f', '': 'Up', '⇤': 'XF86Back', '️': 'Return', '': 'Down', '': 'Left', '': 'Right', '': 'm', }, }, 'YouTube': { commands: { '⇤': 'shift+p', '⇥': 'shift+n', '': 'Up', 'CC': 'c', '️': 'k', '': 'Down', '': 'j', '': 'l', '': 'm', }, }, 'Amazon Prime Video': { window_name_override: 'Prime Video', commands: { '⇤': 'Escape', '+': 'f', '': 'Up', 'CC': 'c', '️': 'space', '': 'Down', '': 'Left', '': 'Right', '': 'm', }, }, 'Generic / Music Player': { window_name_override: '', commands: { '⇤': 'XF86AudioPrev', '⇥': 'XF86AudioNext', '': 'XF86AudioRaiseVolume', '': 'r', '️': 'XF86AudioPlay', '': 'XF86AudioLowerVolume', '': 'Left', '': 'Right', '': 'XF86AudioMute', }, }, };

Wartości kodów klawiszy można znaleźć za pomocą xev . (Dla mnie „wyciszenie dźwięku” i „odtwarzanie dźwięku” nie były wykrywalne za pomocą tej metody, więc sprawdziłem również listę kluczy multimedialnych).

Czytelnicy mogą zauważyć różnicę między space a zwrotem — niezależnie od przyczyny, ten szczegół musi być honorowany, aby Return xdotool poprawnie. W związku z tym mamy kilka definicji napisanych wprost — np. shift+p , mimo że P również zadziała — tylko po to, aby nasze intencje były jasne.

Samouczek dotyczący back-endu Node.js, Krok 4: Punkt końcowy „klucza” naszego API (przepraszam za kalambur)

Będziemy potrzebować punktu końcowego do POST , który z kolei będzie symulował naciśnięcia klawiszy za pomocą xdotool . Ponieważ będziemy mieć różne grupy kluczy, które możemy wysłać (po jednym dla każdej usługi), wywołamy punkt końcowy dla konkretnej jednej group . Zmienimy przeznaczenie wygenerowanego punktu końcowego users , zmieniając nazwę routes/users.js na routes/group.js i wprowadzając odpowiednie zmiany w app.js :

 // ... var indexRouter = require('./routes/index'); var groupRouter = require('./routes/group'); // ... app.use('/', indexRouter); app.use('/group', groupRouter); // ...

Kluczową funkcjonalnością jest użycie xdotool poprzez wywołanie powłoki systemowej w routes/group.js . Na razie zakodujemy YouTube jako menu z wyboru, tylko do celów testowych.

 const express = require('express'); const router = express.Router(); const debug = require('debug')('app'); const cp = require('child_process'); const preset_commands = require('../common/preset_commands'); /* POST keystroke to simulate */ router.post('/', function(req, res, next) { const keystroke_name = req.body.keystroke_name; const keystroke_code = preset_commands['YouTube'].commands[keystroke_name]; const final_command = `xdotool \ search "YouTube" \ windowactivate --sync \ key --clearmodifiers ${keystroke_code}`; debug(`Executing ${final_command}`); cp.exec(final_command, (err, stdout, stderr) => { debug(`Executed ${keystroke_name}`); return res.redirect(req.originalUrl); }); }); module.exports = router;

Tutaj pobieramy żądany klucz „name” z treści żądania POST ( req.body ) pod parametrem o nazwie keystroke_name . To będzie coś w . Następnie używamy tego do wyszukania odpowiedniego kodu z obiektu commands preset_commands['YouTube'] .

Ostatnie polecenie znajduje się w więcej niż jednej linii, więc \ s na końcu każdej linii łączy wszystkie elementy w jedno polecenie:

  • search "YouTube" pobiera pierwsze okno z „YouTube” w tytule.
  • windowactivate --sync aktywuje pobrane okno i czeka, aż będzie gotowe do odebrania naciśnięcia klawisza.
  • key --clearmodifiers ${keystroke_code} wysyła naciśnięcie klawisza, upewniając się, że tymczasowo wyczyściłeś klawisze modyfikujące, takie jak Caps Lock, które mogą zakłócać to, co wysyłamy.

W tym momencie kod zakłada, że ​​dostarczamy mu prawidłowe dane wejściowe — na co będziemy bardziej ostrożni później.

Dla uproszczenia, kod zakłada również, że otwarte jest tylko jedno okno aplikacji z napisem „YouTube” w tytule — jeśli jest więcej niż jedno dopasowanie, nie ma gwarancji, że wyślemy naciśnięcia klawiszy do zamierzonego okna. Jeśli jest to problem, może pomóc to, że tytuły okien można zmienić po prostu, przełączając zakładki przeglądarki we wszystkich oknach oprócz tego, które ma być zdalnie sterowane.

Gdy wszystko jest gotowe, możemy ponownie uruchomić nasz serwer, ale tym razem z włączonym debugowaniem, dzięki czemu możemy zobaczyć wyniki naszych wywołań debug . Aby to zrobić, po prostu uruchom DEBUG=old-fashioned-remote:* yarn start lub DEBUG=old-fashioned-remote:* npm start . Po uruchomieniu odtwórz wideo na YouTube, otwórz kolejne okno terminala i spróbuj wywołać cURL:

 curl --data "keystroke_name=️" http://localhost:3000/group

To wysyła żądanie POST z żądaną nazwą naciśnięcia klawisza w swoim ciele do naszej lokalnej maszyny na porcie 3000 , na którym nasłuchuje nasz backend. Uruchomienie tego polecenia powinno spowodować wyświetlenie w oknie npm notatek o Executing i Executed , a co ważniejsze, wywołanie przeglądarki i wstrzymanie jej wideo. Ponowne wykonanie tego polecenia powinno dać te same dane wyjściowe i wznowić je.

Samouczek zaplecza Node.js, krok 5: Wiele układów zdalnego sterowania

Nasz backend nie jest do końca skończony. Potrzebujemy go również, aby móc:

  1. Utwórz listę układów pilota zdalnego sterowania z preset_commands .
  2. Utwórz listę „nazw” naciśnięć klawiszy od momentu, gdy wybraliśmy konkretny układ pilota. (Mogliśmy również wybrać użycie common/preset_commands.js bezpośrednio na interfejsie użytkownika, ponieważ jest to już JavaScript i jest tam filtrowany. To jedna z potencjalnych zalet zaplecza Node.js, po prostu nie używamy go tutaj .)

Obie te funkcje są miejscem, w którym nasz samouczek dotyczący zaplecza Node.js przecina się z interfejsem opartym na programie Pug, który będziemy budować.

Używanie szablonów mopsów do prezentowania listy pilotów

Back-endowa część równania oznacza modyfikację routes/index.js tak, aby wyglądała tak:

 const express = require('express'); const router = express.Router(); const preset_commands = require('../common/preset_commands'); /* GET home page. */ router.get('/', function(req, res, next) { const group_names = Object.keys(preset_commands); res.render('index', { title: 'Which Remote?', group_names, portrait_css: `.group_bar { height: calc(100%/${Math.min(4, group_names.length)}); line-height: calc(100vh/${Math.min(4, group_names.length)}); }`, landscape_css: `.group_bar { height: calc(100%/${Math.min(2, group_names.length)}); line-height: calc(100vh/${Math.min(2, group_names.length)}); }`, }); }); module.exports = router;

Tutaj pobieramy nazwy układów zdalnego sterowania ( group_names ), wywołując Object.keys w naszym pliku preset_commands . Następnie wysyłamy je i kilka innych potrzebnych nam danych do silnika szablonów Pug, który jest automatycznie wywoływany przez res.render() .

Uważaj, aby nie pomylić znaczenia keys tutaj z uderzeniami klawiszy, które wysyłamy: Object.keys daje nam tablicę (uporządkowaną listę) zawierającą wszystkie klucze par klucz-wartość , które tworzą obiekt w JavaScript:

 const my_object = { 'a key' : 'its corresponding value' , 'another key' : 'its separate corresponding value' , };

Jeśli spojrzymy na common/preset_commands.js , zobaczymy powyższy wzorzec, a naszymi kluczami (w sensie obiektowym) są nazwy naszych grup: 'Netflix' , 'YouTube' itp. Ich odpowiadające wartości nie są proste łańcuchy, takie jak my_object powyżej — same w sobie są całymi obiektami, z własnymi kluczami, tj. commands i prawdopodobnie window_name_override .

Przekazywany tutaj niestandardowy CSS jest, wprawdzie, trochę hackem. Powodem, dla którego w ogóle tego potrzebujemy, zamiast korzystać z nowoczesnego rozwiązania opartego na flexboksie, jest lepsza kompatybilność ze wspaniałym światem przeglądarek mobilnych w ich jeszcze wspanialszych starszych wcieleniach. W tym przypadku najważniejsze jest to, że w trybie poziomym utrzymujemy duże przyciski, wyświetlając nie więcej niż dwie opcje na cały ekran; w trybie portretowym cztery.

Ale gdzie tak naprawdę zostaje zamieniony na kod HTML, który zostanie wysłany do przeglądarki? W tym miejscu pojawia się views/index.pug , które będziemy chcieli wyglądać tak:

 extends layout block header_injection style(media='(orientation: portrait)') #{portrait_css} style(media='(orientation: landscape)') #{landscape_css} block content each group_name in group_names span(class="group_bar") a(href='/group/?group_name=' + group_name) #{group_name}

Ważna jest pierwsza linia: extends layout oznacza, że ​​Pug będzie brał ten plik w kontekście views/layout.pug , który jest rodzajem szablonu nadrzędnego, którego będziemy używać ponownie tutaj, a także w innym widoku. Musimy dodać kilka wierszy po wierszu link , aby końcowy plik wyglądał tak:

 doctype html html head title= title link(rel='stylesheet', href='/stylesheets/style.css') block header_injection meta(name='viewport', content='user-scalable=no') body block content

Nie zagłębimy się tutaj w podstawy HTML, ale dla niezaznajomionych z nimi czytelników, ten kod Pug odzwierciedla standardowy kod HTML, który można znaleźć prawie wszędzie. Aspekt szablonowy zaczyna się od title= title , który ustawia tytuł HTML na dowolną wartość odpowiadającą kluczowi title obiektu, który przekazujemy Pugowi przez res.render .

Możemy zobaczyć inny aspekt szablonowania dwóch wierszy później za pomocą block , który nazywamy header_injection . Takie bloki to symbole zastępcze, które można zastąpić szablonami rozszerzającymi obecny. (Niezwiązana, meta -linia jest po prostu szybkim obejściem przeglądarek mobilnych, więc gdy użytkownicy klikają kontrolki głośności kilka razy z rzędu, telefon powstrzymuje się od powiększania lub pomniejszania.)

Wracając do naszych block : Dlatego views/index.pug definiuje własne block o tych samych nazwach, które można znaleźć w views/layout.pug . W tym przypadku header_injection , pozwala nam to użyć CSS specyficznego dla orientacji pionowej lub poziomej, w której będzie telefon.

content to miejsce, w którym umieszczamy główną widoczną część strony, czyli w tym przypadku:

  1. Pętle przez tablicę group_names przekazujemy ją,
  2. tworzy element <span> dla każdego z zaaplikowaną klasą CSS group_bar i
  3. tworzy łącze w każdym <span> na podstawie nazwy group_name .

Klasę CSS group_bar możemy zdefiniować w pliku pobranym przez views/layout.pug , a mianowicie public/stylesheets/style.css :

 html, body, form { padding: 0; margin: 0; height: 100%; font: 14px "Lucida Grande", Helvetica, Arial, sans-serif; } .group_bar, .group_bar a, .remote_button { box-sizing: border-box; border: 1px solid white; color: greenyellow; background-color: black; } .group_bar { width: 100%; font-size: 6vh; text-align: center; display: inline-block; } .group_bar a { text-decoration: none; display: block; }

W tym momencie, jeśli npm start nadal działa, przejście do http://localhost:3000/ w przeglądarce na komputerze stacjonarnym powinno pokazać dwa bardzo duże przyciski dla Netflix i YouTube, a reszta jest dostępna po przewinięciu w dół.

Test selektora układu pilota przy użyciu przeglądarki komputerowej, pokazujący dwa bardzo duże przyciski dla Netflix i YouTube.

Ale jeśli klikniemy je w tym momencie, nie będą działać, ponieważ nie zdefiniowaliśmy jeszcze trasy, do której się prowadzą (pozycja GET w /group ).

Pokazywanie wybranego układu pilota

Aby to zrobić, dodamy to do routes/group.js tuż przed ostatnią linią module.exports :

 router.get('/', function(req, res, next) { const group_name = req.query.group_name || ''; const group = preset_commands[group_name]; return res.render('group', { keystroke_names: Object.keys(group.commands), group_name, title: `${group_name.match(/([AZ])/g).join('')}-Remote` }); });

Spowoduje to wysłanie nazwy grupy do punktu końcowego (np. przez umieszczenie ?group_name=Netflix na końcu /group/ ) i użycie tego do pobrania wartości commands z odpowiedniej grupy. Ta wartość ( group.commands ) jest obiektem, a kluczami tego obiektu są nazwy ( keystroke_names ), które wyświetlimy na naszym układzie zdalnego sterowania.

Uwaga: niedoświadczeni programiści nie będą musieli wchodzić w szczegóły, jak to działa, ale wartość title używa nieco wyrażeń regularnych, aby przekształcić nazwy naszych grup/układów w akronimy — na przykład nasz pilot YouTube będzie miał tytuł przeglądarki YT-Remote . W ten sposób, jeśli debugujemy na naszym komputerze głównym przed wypróbowaniem rzeczy na telefonie, nie będziemy mieć xdotool , który przechwytuje samo okno przeglądarki zdalnego sterowania, zamiast tego, które próbujemy kontrolować. Tymczasem w naszych telefonach tytuł będzie ładny i krótki, gdybyśmy chcieli dodać pilota do zakładek.

Podobnie jak w przypadku naszego poprzedniego spotkania z res.render , ten wysyła swoje dane do połączenia z szablonem views/group.pug . Stworzymy ten plik i wypełnimy go tym:

 extends layout block header_injection script(type='text/javascript', src='/javascript/group-client.js') block content form(action="/group?group_name=" + group_name, method="post") each keystroke_name in keystroke_names input(type="submit", name="keystroke_name", value=keystroke_name, class="remote_button")

Podobnie jak w przypadku views/index.pug , zastępujemy dwa blogi z views/layout.pug . Tym razem to nie CSS umieszczamy w nagłówku, ale jakiś JavaScript po stronie klienta, do którego wkrótce przejdziemy. (I tak, w chwili przekory zmieniłem nazwy niepoprawnie zwielokrotnionych javascripts …)

Główną content jest tutaj formularz HTML złożony z kilku różnych przycisków przesyłania, po jednym dla każdego keystroke_name . Każdy przycisk przesyła formularz (wykonując żądanie POST ) przy użyciu nazwy naciśnięcia klawisza, którą wyświetla jako wartość wysyłaną z formularzem.

Potrzebujemy również trochę więcej CSS w naszym głównym pliku arkusza stylów:

 .remote_button { float: left; width: calc(100%/3); height: calc(100%/3); font-size: 12vh; }

Wcześniej, po skonfigurowaniu punktu końcowego, zakończyliśmy obsługę żądania za pomocą:

 return res.redirect(req.originalUrl);

W praktyce oznacza to, że gdy przeglądarka przesyła formularz, zaplecze Node.js odpowiada, mówiąc przeglądarce, aby wróciła do strony, z której formularz został przesłany — tj. do głównego układu pilota zdalnego sterowania. Byłoby bardziej elegancko bez przełączania stron; jednak chcemy maksymalnej kompatybilności z dziwnym i cudownym światem zgrzybiałych przeglądarek mobilnych. W ten sposób, nawet bez działającego żadnego frontendowego JavaScriptu, nasz projekt zaplecza Node.js powinien nadal działać.

Dash frontendowego JavaScript

Wadą korzystania z formularza do przesyłania naciśnięć klawiszy jest to, że przeglądarka musi czekać, a następnie wykonać dodatkową podróż w obie strony: strona i jej zależności muszą być następnie zażądane z naszego zaplecza Node.js i dostarczone. Następnie muszą zostać ponownie wyrenderowane przez przeglądarkę.

Czytelnicy mogą się zastanawiać, jaki może to mieć wpływ. W końcu strona jest malutka, jej zależności są niezwykle minimalne, a nasz ostatni projekt Node.js będzie działał przez lokalne połączenie Wi-Fi. Powinna to być konfiguracja o niskim opóźnieniu, prawda?

Jak się okazuje – przynajmniej podczas testowania na starszych smartfonach z systemem Windows Phone 8.1 i Android 4.4.2 – efekt jest niestety dość zauważalny w częstym przypadku szybkiego stukania w celu zwiększenia lub zmniejszenia głośności odtwarzania o kilka stopni. Oto, w czym może pomóc JavaScript, nie rezygnując z naszej pełnej wdzięku funkcji zastępowania ręcznego POST -ów za pomocą formularzy HTML.

W tym momencie nasz końcowy klient JavaScript (do umieszczenia w public/javascript/group-client.js ) musi być zgodny ze starymi, nieobsługiwanymi już przeglądarkami mobilnymi. Ale nie potrzebujemy go dużo:

 (function () { function form_submit(event) { var request = new XMLHttpRequest(); request.open('POST', window.location.pathname + window.location.search, true); request.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); request.send('keystroke_name=' + encodeURIComponent(event.target.value)); event.preventDefault(); } window.addEventListener("DOMContentLoaded", function() { var inputs = document.querySelectorAll("input"); for (var i = 0; i < inputs.length; i++) { inputs[i].addEventListener("click", form_submit); } }); })();

W tym przypadku funkcja form_submit po prostu wysyła dane za pośrednictwem wywołania asynchronicznego, a ostatnia linia zapobiega normalnemu zachowaniu przeglądarki podczas wysyłania, dzięki czemu nowa strona ładuje się na podstawie odpowiedzi serwera. Druga połowa tego fragmentu po prostu czeka, aż strona się załaduje, a następnie podłącza każdy przycisk przesyłania, aby użyć form_submit . Całość owinięta jest w IIFE.

Ostatnie poprawki

W końcowej wersji naszego samouczka zaplecza Node.js wprowadzono wiele zmian w powyższych fragmentach, głównie w celu lepszej obsługi błędów:

  • Backend Node.js sprawdza teraz nazwy grup i wysyłane do niego naciśnięcia klawiszy, aby upewnić się, że istnieją. Ten kod znajduje się w funkcji, która jest ponownie wykorzystywana zarówno dla funkcji GET , jak i POST routes/group.js .
  • Używamy szablonu error Mops, jeśli nie.
  • Front-end JavaScript i CSS teraz powodują, że przyciski tymczasowo są zaznaczone na szaro podczas oczekiwania na odpowiedź z serwera, na zielono, gdy tylko sygnał przeszedł przez xdotool i z powrotem bez problemów, a na czerwono, jeśli coś nie działało zgodnie z oczekiwaniami .
  • Back-end Node.js wypisze ślad stosu, jeśli umrze, co będzie mniej prawdopodobne, biorąc pod uwagę powyższe.

Czytelnicy mogą przejrzeć (i/lub sklonować) cały projekt Node.js na GitHub.

Samouczek zaplecza Node.js, Krok 5: Test w prawdziwym świecie

Czas wypróbować go na prawdziwym telefonie podłączonym do tej samej sieci Wi-Fi, co host, na którym działa npm start i odtwarzacz filmów lub muzyki. To tylko kwestia wskazania w przeglądarce internetowej smartfona lokalnego adresu IP hosta (z dodanym :3000 ), który prawdopodobnie najłatwiej znaleźć, uruchamiając hostname -I | awk '{print $1}' hostname -I | awk '{print $1}' w terminalu na hoście.

Jednym z problemów, które użytkownicy Windows Phone 8.1 mogą zauważyć, jest to, że próba przejścia do czegoś takiego jak 192.168.2.5:3000 spowoduje wyświetlenie wyskakującego okienka błędu:

Zrzut ekranu z komunikatem o błędzie systemu Windows Phone zatytułowanym „Nieobsługiwany adres” z informacją „Internet Explorer Mobile nie obsługuje tego typu adresu i nie może wyświetlić tej strony.

Na szczęście nie trzeba się zniechęcać: wystarczy dodać przedrostek http:// lub dodać końcówkę / , aby pobrać adres bez dalszych skarg.

Ekran wyboru układu pilota.

Wybór opcji tam powinien doprowadzić nas do działającego pilota.

Ekran pilota zdalnego sterowania „Odtwarzacz ogólny/odtwarzacz muzyki”.

Dla większej wygody użytkownicy mogą chcieć dostosować ustawienia DHCP routera, aby zawsze przypisywać ten sam adres IP do hosta i dodać zakładkę do ekranu wyboru układu i/lub dowolnych ulubionych układów.

Witaj w żądaniach ściągnięcia

Prawdopodobnie nie wszystkim spodoba się ten projekt dokładnie taki, jaki jest. Oto kilka pomysłów na ulepszenia dla tych, którzy chcą zagłębić się w kod:

  • Poprawienie układów lub dodanie nowych dla innych usług, takich jak Disney Plus, powinno być proste.
  • Może niektórzy woleliby układ „tryb światła” i opcję przełączania się między nimi.
  • Wycofanie się z Netflix, ponieważ jest nieodwracalne, może naprawdę użyć „czy na pewno?” jakieś potwierdzenie.
  • Projekt z pewnością skorzystałby na wsparciu systemu Windows.
  • xdotool wspomina o OSX — czy ten (lub może ten) projekt działa na współczesnym komputerze Mac?
  • Zaawansowane wylegiwanie się, sposób na wyszukiwanie i przeglądanie filmów, zamiast wybierania jednego filmu Netflix/Amazon Prime Video lub tworzenia listy odtwarzania na YouTube na komputerze.
  • Zautomatyzowany zestaw testów na wypadek, gdyby którakolwiek z sugerowanych zmian złamała pierwotną funkcjonalność.

Mam nadzieję, że podobał Ci się ten samouczek dotyczący zaplecza Node.js i dzięki temu poprawiono obsługę multimediów. Miłego przesyłania strumieniowego — i kodowania!

Powiązane: Budowanie interfejsu API REST Node.js/TypeScript, część 1: Express.js