Codificación de Cabin Fever: un tutorial de back-end de Node.js
Publicado: 2022-03-11El encierro de COVID-19 tiene a muchos de nosotros atrapados en casa, tal vez con la esperanza de que la fiebre de la cabina sea el peor tipo de fiebre que experimentaremos. Muchos de nosotros estamos consumiendo más contenido de video que nunca. Si bien el ejercicio es especialmente importante en este momento, a veces, hay nostalgia por el lujo de un buen control remoto antiguo cuando la computadora portátil está fuera de su alcance.
Ahí es donde entra este proyecto: la oportunidad de transformar cualquier teléfono inteligente, incluso uno viejo que de otro modo sería inútil por falta de actualizaciones, en un práctico control remoto para el próximo Netflix/YouTube/Amazon Prime Video/etc. binge-watch. También es un tutorial de back-end de Node.js: una oportunidad de aprender los conceptos básicos de JavaScript de back-end utilizando el marco Express y el motor de plantillas Pug (anteriormente Jade).
Si eso suena desalentador, el proyecto completo de Node.js se presentará al final; los lectores solo necesitan aprender tanto como estén interesados en aprender, y habrá una buena cantidad de explicaciones más suaves de algunos conceptos básicos en el camino que los lectores más experimentados pueden omitir.
¿Por qué no solo...?
Los lectores pueden preguntarse: "¿Por qué codificar un back-end de Node.js?" (Aparte de la oportunidad de aprender, por supuesto.) "¿No existe ya una aplicación para eso?"
Claro, muchos de ellos. Pero hay dos razones principales por las que esto puede no ser deseable:
- Para aquellos que intentan reutilizar un teléfono antiguo, es posible que ya no sea una opción , como es el caso con el dispositivo Windows Phone 8.1 que quería usar. (La tienda de aplicaciones se cerró oficialmente a fines de 2019).
- Confianza (o falta de ella). Al igual que muchas aplicaciones que se encuentran en cualquier plataforma móvil, a menudo vienen con el requisito de que los usuarios otorguen muchos más permisos de los que la aplicación necesita para lo que pretende hacer. Pero incluso si este aspecto está adecuadamente limitado, la naturaleza de una aplicación de control remoto significa que los usuarios aún deben confiar en que los desarrolladores de aplicaciones no están abusando de sus privilegios en el escritorio de la solución al incluir spyware u otro malware.
Estos problemas han existido durante mucho tiempo e incluso fueron la motivación para un proyecto similar de 2014 que se encuentra en GitHub. nvm
facilita la instalación de versiones anteriores de Node.js, e incluso si algunas dependencias necesitaban actualizarse, Node.js tenía una gran reputación por ser compatible con versiones anteriores.
Desafortunadamente, bitrot ganó. Un enfoque obstinado y la compatibilidad de back-end de Node.js no fueron rival para las interminables depreciaciones y los bucles de dependencia imposibles entre las versiones antiguas de Grunt, Bower y docenas de otros componentes. Horas más tarde, estaba más que claro que sería mucho más fácil empezar de cero, a pesar del propio consejo de este autor de no reinventar la rueda.
Nuevos artilugios de antaño: reutilización de teléfonos como controles remotos mediante un back-end de Node.js
En primer lugar, tenga en cuenta que este proyecto de Node.js actualmente es específico para Linux, desarrollado y probado en Linux Mint 19 y Linux Mint 19.3, en particular, pero sin duda se podría agregar soporte para otras plataformas. Es posible que ya funcione en una Mac.
Suponiendo que se instala una versión moderna de Node.js y se abre un símbolo del sistema en un nuevo directorio que servirá como raíz del proyecto, estamos listos para comenzar con Express:
npx express-generator --view=pug
Nota: Aquí, npx
es una herramienta útil que viene con npm
, el administrador de paquetes de Node.js que se envía con Node.js. Lo estamos usando para ejecutar el generador de esqueleto de aplicaciones de Express. En el momento de escribir este artículo, el generador crea un proyecto Express/Node.js que, de forma predeterminada, sigue incorporando un motor de plantillas llamado Jade, aunque el proyecto Jade se renombró a sí mismo como "Pug" desde la versión 2.0 en adelante. Entonces, para estar al día y usar Pug de inmediato, además de evitar las advertencias de obsolescencia, --view=pug
, una opción de línea de comandos para el script express-generator
que ejecuta npx
.
Una vez hecho esto, debemos instalar algunos paquetes de la lista de dependencias recién poblada de nuestro proyecto Node.js en package.json
. La forma tradicional de hacer esto es ejecutar npm i
( i
para "instalar"). Pero algunos todavía prefieren la velocidad de Yarn, así que si lo tienes instalado, simplemente ejecuta yarn
sin parámetros.
En este caso, debería ser seguro ignorar la advertencia de desaprobación (con suerte pronto se solucionará) de una de las subdependencias de Pug, siempre que el acceso se mantenga según sea necesario en la red local.
Un inicio rápido de yarn start
o npm start
, seguido de una navegación a localhost:3000
en un navegador, muestra que nuestro back-end básico de Node.js basado en Express funciona. Podemos matarlo con Ctrl+C
.
Tutorial de back-end de Node.js, paso 2: cómo enviar pulsaciones de teclas en la máquina host
Con la parte remota ya hecha a la mitad, dirijamos nuestra atención a la parte de control . Necesitamos algo que pueda controlar mediante programación la máquina en la que ejecutaremos nuestro back-end de Node.js, fingiendo que está presionando teclas en el teclado.
Para eso, instalaremos xdotool
usando sus instrucciones oficiales. Una prueba rápida de su comando de ejemplo en una terminal:
xdotool search "Mozilla Firefox" windowactivate --sync key --clearmodifiers ctrl+l
…debería hacer exactamente lo que dice, suponiendo que Mozilla Firefox esté abierto en ese momento. ¡Eso es bueno! Es fácil hacer que nuestro proyecto Node.js llame a herramientas de línea de comandos como xdotool
, como veremos pronto.
Tutorial de back-end de Node.js, paso 3: diseño de características
Esto puede no ser cierto para todos, pero personalmente, encuentro que muchos controles remotos físicos modernos tienen aproximadamente cinco veces más botones de los que usaré. Entonces, para este proyecto, buscamos un diseño de pantalla completa con una cuadrícula de tres por tres de botones agradables, grandes y fáciles de usar. Depende de la preferencia personal cuáles pueden ser esos nueve botones.
Resulta que los atajos de teclado incluso para las funciones más simples no son idénticos en Netflix, YouTube y Amazon Prime Video. Estos servicios tampoco funcionan con claves multimedia genéricas como es probable que lo haga una aplicación de reproductor de música nativa. Además, es posible que ciertas funciones no estén disponibles con todos los servicios.
Entonces, lo que tendremos que hacer es definir un diseño de control remoto diferente para cada servicio y proporcionar una forma de cambiar entre ellos.
Definición de diseños de control remoto y asignación de ellos a atajos de teclado
Hagamos un prototipo rápido que funcione con un puñado de ajustes preestablecidos. Los pondremos en common/preset_commands.js
—“common” porque incluiremos estos datos de más de un archivo:
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', }, }, };
Los valores del código clave se pueden encontrar usando xev
. (Para mí, los de "silencio de audio" y "reproducción de audio" no se podían detectar con este método, por lo que también consulté una lista de teclas de medios).
Los lectores pueden notar la diferencia entre mayúsculas y minúsculas entre space
y Return
; independientemente de la razón de esto, se debe respetar este detalle para que xdotool
funcione correctamente. Relacionado con esto, tenemos un par de definiciones escritas explícitamente, por ejemplo, shift+p
aunque P
también funcionaría, solo para mantener nuestras intenciones claras.
Tutorial de back-end de Node.js, paso 4: punto final "clave" de nuestra API (perdón por el juego de palabras)
Necesitaremos un punto final para POST
, que a su vez simulará pulsaciones de teclas usando xdotool
. Dado que tendremos diferentes grupos de claves que podemos enviar (una para cada servicio), llamaremos al extremo de un group
en particular. Reutilizaremos el punto final de los users
generados cambiando el nombre de routes/users.js
a route routes/group.js
y realizando los cambios correspondientes en app.js
:
// ... var indexRouter = require('./routes/index'); var groupRouter = require('./routes/group'); // ... app.use('/', indexRouter); app.use('/group', groupRouter); // ...
La funcionalidad clave es usar xdotool
a través de una llamada de shell del sistema en routes/group.js
. Codificaremos YouTube
como el menú de elección por el momento, solo con fines de prueba.
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;
Aquí, tomamos el "nombre" de la clave solicitada del cuerpo de la solicitud POST
( req.body
) bajo el parámetro llamado keystroke_name
. Eso será algo como ️
. Luego lo usamos para buscar el código correspondiente del objeto de commands
de preset_commands['YouTube']
.
El comando final está en más de una línea, por lo que \
s al final de cada línea une todas las piezas en un solo comando:
-
search "YouTube"
obtiene la primera ventana con "YouTube" en el título. -
windowactivate --sync
activa la ventana recuperada y espera hasta que esté lista para recibir una pulsación de tecla. -
key --clearmodifiers ${keystroke_code}
envía la pulsación de tecla, asegurándose de borrar temporalmente las teclas modificadoras como Bloq Mayús que pueden interferir con lo que estamos enviando.
En este punto, el código asume que le estamos dando una entrada válida, algo de lo que tendremos más cuidado más adelante.
Para simplificar, el código también asumirá que solo hay una ventana de aplicación abierta con "YouTube" en su título; si hay más de una coincidencia, no hay garantía de que enviaremos pulsaciones de teclas a la ventana deseada. Si eso es un problema, puede ayudar que los títulos de las ventanas se puedan cambiar simplemente cambiando las pestañas del navegador en todas las ventanas además de la que se controlará de forma remota.
Con eso listo, podemos iniciar nuestro servidor nuevamente, pero esta vez con la depuración habilitada para que podamos ver el resultado de nuestras llamadas de debug
. Para hacerlo, simplemente ejecute DEBUG=old-fashioned-remote:* yarn start
o DEBUG=old-fashioned-remote:* npm start
. Una vez que se esté ejecutando, reproduzca un video en YouTube, abra otra ventana de terminal e intente una llamada cURL:
curl --data "keystroke_name=️" http://localhost:3000/group
Eso envía una solicitud POST
con el nombre de la pulsación de tecla solicitada en su cuerpo a nuestra máquina local en el puerto 3000
, el puerto en el que escucha nuestro back-end. Ejecutar ese comando debería generar notas sobre Executing
y npm
Executed
, lo que es más importante, abrir el navegador y pausar su video. Ejecutar ese comando nuevamente debería dar el mismo resultado y reanudarlo.
Tutorial de back-end de Node.js, paso 5: Múltiples diseños de control remoto
Nuestro back-end no está del todo terminado. También lo necesitaremos para poder:
- Produce una lista de diseños de control remoto desde
preset_commands
. - Produzca una lista de "nombres" de pulsaciones de teclas una vez que hayamos elegido un diseño de control remoto en particular. (También podríamos haber optado por usar
common/preset_commands.js
directamente en el front-end, ya que es JavaScript y filtrado allí. Esa es una de las ventajas potenciales de un back-end de Node.js, simplemente no lo usamos aquí .)
Ambas características son donde nuestro tutorial de back-end de Node.js se cruza con el front-end basado en Pug que construiremos.
Uso de plantillas Pug para presentar una lista de controles remotos
La parte de back-end de la ecuación significa modificar routes/index.js
para que se vea así:
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;
Aquí, tomamos nuestros nombres de diseño de control remoto ( group_names
) llamando a Object.keys
en nuestro archivo preset_commands
. Luego los enviamos junto con algunos otros datos que necesitaremos al motor de plantillas Pug que se llama automáticamente a través de res.render()
.
Tenga cuidado de no confundir el significado de las keys
aquí con las Object.keys
nos da una matriz (una lista ordenada) que contiene todas las claves de los pares clave-valor que componen un objeto en JavaScript:

const my_object = { 'a key' : 'its corresponding value' , 'another key' : 'its separate corresponding value' , };
Si observamos common/preset_commands.js
, veremos el patrón anterior, y nuestras claves (en el sentido de objeto) son los nombres de nuestros grupos: 'Netflix'
, 'YouTube'
, etc. Sus valores correspondientes no son cadenas simples como my_object
tiene arriba: son objetos completos en sí mismos, con sus propias claves, es decir, commands
y posiblemente window_name_override
.
El CSS personalizado que se pasa aquí es, sin duda, un poco complicado. La razón por la que lo necesitamos en lugar de utilizar una solución moderna basada en flexbox es para una mejor compatibilidad con el maravilloso mundo de los navegadores móviles en sus encarnaciones más antiguas aún más maravillosas. En este caso, lo principal a tener en cuenta es que en el modo horizontal, mantenemos los botones grandes al mostrar no más de dos opciones por pantalla completa; en modo retrato, cuatro.
Pero, ¿dónde se convierte eso realmente en HTML para enviarlo al navegador? Ahí es donde entra en views/index.pug
, que queremos que se vea así:
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}
La primera línea es importante: extends layout
significa que Pug tomará este archivo en el contexto de views/layout.pug
, que es una especie de plantilla principal que reutilizaremos aquí y también en otra vista. Tendremos que agregar un par de líneas después de la línea de link
para que el archivo final se vea así:
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
No entraremos en los conceptos básicos de HTML aquí, pero para los lectores que no estén familiarizados con ellos, este código Pug refleja el código HTML estándar que se encuentra en casi todas partes. El aspecto de plantilla comienza con title= title
, que establece el título HTML en cualquier valor correspondiente a la clave de title
del objeto que pasamos a Pug a través res.render
.
Podemos ver un aspecto diferente de la creación de plantillas dos líneas más adelante con un block
que llamamos header_injection
. Bloques como estos son marcadores de posición que pueden ser reemplazados por plantillas que amplían la actual. (Sin relación, la línea meta
es simplemente una solución rápida para los navegadores móviles, por lo que cuando los usuarios tocan los controles de volumen varias veces seguidas, el teléfono se abstiene de acercar o alejar).
Volviendo a nuestros block
: Esta es la razón por la que views/index.pug
define sus propios block
con los mismos nombres que se encuentran en views/layout.pug
. En este caso de header_injection
, esto nos permite usar CSS específico para las orientaciones vertical u horizontal en las que estará el teléfono.
El content
es donde ponemos la parte visible principal de la página web, que en este caso:
- Recorre la matriz
group_names
que le pasamos, - crea un elemento
<span>
para cada uno con la clase CSSgroup_bar
aplicada, y - crea un enlace dentro de cada
<span>
basado engroup_name
.
La clase CSS group_bar
que podemos definir en el archivo extraído a través de views/layout.pug
, es decir, 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; }
En este punto, si npm start
aún se está ejecutando, ir a http://localhost:3000/
en un navegador de escritorio debería mostrar dos botones muy grandes para Netflix y YouTube, con el resto disponible al desplazarse hacia abajo.
Pero si hacemos clic en ellos en este punto, no funcionarán, porque aún no hemos definido la ruta a la que se vinculan (el GET
ting de /group
).
Mostrar el diseño de control remoto elegido
Para hacerlo, agregaremos esto a routes/group.js
justo antes de la última línea 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` }); });
Esto hará que el nombre del grupo se envíe al punto final (p. ej., colocando ?group_name=Netflix
al final de /group/
), y lo usará para obtener el valor de los commands
del grupo correspondiente. Ese valor ( group.commands
) es un objeto, y las teclas de ese objeto son los nombres ( keystroke_names
) que mostraremos en nuestro diseño de control remoto.
Nota: los desarrolladores sin experiencia no necesitarán entrar en detalles sobre cómo funciona, pero el valor del title
usa un poco de expresiones regulares para convertir los nombres de nuestro grupo/diseño en acrónimos; por ejemplo, nuestro control remoto de YouTube tendrá el título del navegador. YT-Remote
. De esa manera, si estamos depurando en nuestra máquina anfitriona antes de probar las cosas en un teléfono, no tendremos xdotool
tomando la ventana del navegador de control remoto, en lugar de la que estamos tratando de controlar. Mientras tanto, en nuestros teléfonos, el título será agradable y breve, en caso de que queramos marcar el control remoto.
Al igual que en nuestro encuentro anterior con res.render
, este envía sus datos para que se mezclen con la plantilla views/group.pug
. Crearemos ese archivo y lo llenaremos con esto:
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")
Al igual que con views/index.pug
, reemplazamos los dos blogs de views/layout.pug
. Esta vez, no es CSS lo que estamos poniendo en el encabezado, sino algo de JavaScript del lado del cliente, al que llegaremos en breve. (Y sí, en un momento de perspicacia, cambié el nombre de los javascripts
incorrectamente pluralizados...)
El content
principal aquí es un formulario HTML hecho de varios botones de envío diferentes, uno para cada keystroke_name
. Cada botón envía el formulario (haciendo una solicitud POST
) usando el nombre de la pulsación de tecla que muestra como el valor que envía con el formulario.
También necesitaremos un poco más de CSS en nuestro archivo de hoja de estilo principal:
.remote_button { float: left; width: calc(100%/3); height: calc(100%/3); font-size: 12vh; }
Anteriormente, cuando configuramos el punto final, terminamos de manejar la solicitud con:
return res.redirect(req.originalUrl);
Esto significa que cuando el navegador envía el formulario, el back-end de Node.js responde diciéndole al navegador que regrese a la página desde la que se envió el formulario, es decir, el diseño principal del control remoto. Sería más elegante sin cambiar de página; sin embargo, queremos la máxima compatibilidad con el extraño y maravilloso mundo de los navegadores móviles decrépitos. De esta manera, incluso sin ningún JavaScript de front-end funcionando, nuestro proyecto de back-end de Node.js debería seguir funcionando.
Una pizca de JavaScript front-end
La desventaja de usar un formulario para enviar pulsaciones de teclas es que el navegador tiene que esperar y luego ejecutar un viaje de ida y vuelta adicional: la página y sus dependencias luego deben solicitarse desde nuestro back-end de Node.js y entregarse. Luego, deben ser renderizados nuevamente por el navegador.
Los lectores podrían preguntarse cuánto efecto podría tener esto. Después de todo, la página es pequeña, sus dependencias son extremadamente mínimas y nuestro proyecto final de Node.js se ejecutará a través de una conexión wifi local. Debería ser una configuración de baja latencia, ¿verdad?
Resulta que, al menos cuando se realizan pruebas en teléfonos inteligentes más antiguos con Windows Phone 8.1 y Android 4.4.2, el efecto es lamentablemente bastante notable en el caso común de tocar rápidamente para subir o bajar el volumen de reproducción en algunas muescas. Aquí es donde JavaScript puede ayudar, sin restarle valor a nuestro elegante respaldo de POST
manuales a través de formularios HTML.
En este punto, nuestro JavaScript de cliente final (que se colocará en public/javascript/group-client.js
) debe ser compatible con los navegadores móviles antiguos que ya no son compatibles. Pero no necesitamos mucho de eso:
(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); } }); })();
Aquí, la función form_submit
simplemente envía los datos a través de una llamada asíncrona, y la última línea evita el comportamiento de envío normal de los navegadores, por lo que se carga una nueva página según la respuesta del servidor. La segunda mitad de este fragmento simplemente espera hasta que se carga la página y luego conecta cada botón de envío para usar form_submit
. Todo está envuelto en un IIFE.
Toques finales
Hay una serie de cambios en los fragmentos anteriores en la versión final de nuestro código tutorial de back-end de Node.js, principalmente con el fin de mejorar el manejo de errores:
- El back-end de Node.js ahora verifica los nombres de los grupos y las pulsaciones de teclas que se le envían para asegurarse de que existan. Este código se encuentra en una función que se reutiliza para las funciones
GET
yPOST
deroutes/group.js
. - Hacemos uso de la plantilla de
error
Pug si no lo hacen. - El front-end JavaScript y CSS ahora hacen que los botones se delineen temporalmente en gris mientras esperan una respuesta del servidor, en verde tan pronto como la señal atravesó
xdotool
y regresó sin problemas, y en rojo si algo no funcionó como se esperaba. . - El back-end de Node.js imprimirá un seguimiento de la pila si muere, lo que será menos probable dado lo anterior.
Los lectores pueden examinar (y/o clonar) el proyecto completo de Node.js en GitHub.
Tutorial de back-end de Node.js, paso 5: una prueba del mundo real
Es hora de probarlo en un teléfono real conectado a la misma red wifi que el host que ejecuta npm start
y un reproductor de música o películas. Es solo una cuestión de apuntar el navegador web de un teléfono inteligente a la dirección IP local del host (con el sufijo :3000
), que probablemente sea más fácil de encontrar ejecutando hostname -I | awk '{print $1}'
hostname -I | awk '{print $1}'
en una terminal en el host.
Un problema que los usuarios de Windows Phone 8.1 pueden notar es que intentar navegar a algo como 192.168.2.5:3000
mostrará una ventana emergente de error:
Afortunadamente, no hay necesidad de desanimarse: simplemente con el prefijo http://
o agregando un final /
se obtiene la dirección sin más quejas.
Elegir una opción allí debería llevarnos a un control remoto que funcione.
Para mayor comodidad, es posible que los usuarios deseen ajustar la configuración de DHCP de su enrutador para asignar siempre la misma dirección IP al host y marcar la pantalla de selección de diseño y/o cualquier diseño favorito.
Solicitudes de extracción bienvenidas
Es probable que no a todos les guste este proyecto exactamente como es. Aquí hay algunas ideas de mejoras, para aquellos que quieran profundizar más en el código:
- Debería ser sencillo modificar los diseños o agregar nuevos para otros servicios, como Disney Plus.
- Tal vez algunos prefieran un diseño de "modo de luz" y la opción de cambiar entre ellos.
- Salir de Netflix, ya que no es reversible, realmente podría usar un "¿estás seguro?" confirmación de algún tipo.
- El proyecto seguramente se beneficiaría del soporte de Windows.
- La documentación de
xdotool
menciona OSX: ¿este proyecto (o podría) funcionar en una Mac moderna? - Para un descanso avanzado, una forma de buscar y explorar películas, en lugar de tener que elegir una sola película de Netflix/Amazon Prime Video o crear una lista de reproducción de YouTube en la computadora.
- Un conjunto de pruebas automatizado, en caso de que alguno de los cambios sugeridos rompa la funcionalidad original.
Espero que haya disfrutado este tutorial de back-end de Node.js y, como resultado, una experiencia multimedia mejorada. ¡Feliz transmisión y codificación!