Esta pagina se ve mejor con JavaScript habilitado

Acceso GDrive KODI mediante HTTP

 ·  🎃 kr0m

En otras ocasiones ya hemos explicado como acceder al contenido de GDrive mediante el addon de KODI y como importar contenido mediante FTP . El problema de acceder a contenido 4K desde el addon de KODI es que este resulta muy lento dando problemas como congelaciones momentáneas o retardos indeseados, combinando PlexDrive y exportando el contenido vía FTP funciona razonablemente bien pero mediante la exportación HTTP ganaremos en rendimiento además de que si el servidor está nateado será mas sencillo utilizarlo desde el exterior ya que HTTP utiliza un único puerto.

En este artículo utilizaremos PlexDrive y Nginx para servir el contenido de GDrive a un KODI instalado en AndroidTV.

El artículo se compone de varios pasos:


PlexDrive:

Debemos tener en cuenta que el acceso a GDrive desde PlexDrive es de solo lectura:

Please note that plexdrive doesn't currently support writes (adding new files or modifications), it only supports reading existing files and deletion.

En mi caso voy a utilizar una jail bajo IOCage desde la que importaré el contenido de GDrive y lo serviré por FTP.

Montamos el sistema de ficheros por FUSE en el padre y se lo pasamos a la jail para no tener que modificar permisos especiales en esta.

Instalamos el software necesario:

pkg install fusefs-lkl

Cargamos el módulo fusefs:

kldload fusefs

Ahora debería existir un dispositivo llamado fuse:

ls -la /dev/fuse

crw-rw-rw-  1 root  operator  0xe3 Nov 30 23:28 /dev/fuse

Configuramos el SO para que cargue el módulo de forma automática en cada arranque:

vi /etc/rc.conf

kld_list="fusefs ..."

Averiguamos el drive-id copiándolo desde la URL del navegador mostrada cuando accedemos a GDrive, en mi caso la URL tiene el siguiente aspecto:

https://drive.google.com/drive/folders/0ANN6pzUlNUgXUk9PVA

El drive-id es: 0ANN6pzUlNUgXUk9PVA

Nos bajamos PlexDrive, podemos ver las versiones disponibles en este enlace de github

Creamos el directorio donde montaremos la unidad de GDrive:

mkdir /mnt/gdrive

Arrancamos manualmente PlexDrive:

/root/plexdrive-freebsd-amd64 mount -o allow_other -v 3 –drive-id=0ANN6pzUlNUgXUk9PVA /mnt/gdrive

1. Please go to https://console.developers.google.com/
2. Create a new project
3. Go to library and activate the Google Drive API
4. Go to credentials and create an OAuth client ID
5. Set the application type to 'other'
6. Specify some name and click create
7. Enter your generated client ID: 

Al acceder a la URL mostrada nos aparecerá la siguiente interfaz donde debemos darle al botón de la derecha “CREAR PROYECTO”:

Indicamos un nombre y le damos a crear:

Ya tenemos el proyecto creado:

Activamos la API de Google drive en el proyecto, para ello accedemos a “API y servicios” -> Biblioteca:

Buscamos “Google drive api” y clickamos sobre él:

Le damos a habilitar:

Nos aparecerá la siguiente ventana desde la que debemos crear las credenciales de acceso:

Le damos a “CREAR CREDENCIALES”:

Seleccionamos “ID de cliente OAuth”:

Nos aparecerá la siguiente ventana donde debemos darle a “CONFIGURAR PANTALLA DE CONSENTIMIENTO”:

En User Type indicamos: Externos y le damos a “CREAR”:

Rellenamos los datos obligarotios del formulario y le damos a “GUARDAR Y CONTINUAR”:


No necesitamos ningún tipo de permiso especial, así que le damos a “GUARDAR Y CONTINUAR”:

Agregamos un usuario, le damos a “ADD USER”:

Indicamos el usuario y le damos a “AGREGAR”:

Debería de aparecer en la lista, le damos a “GUARDAR Y CONTINUAR”:

Aparecerá un resumen, le damos a “VOLVER AL PANEL”:


Nos llevará a la “Pantalla de consentimiento de OAuth”, desde aquí accedemos a la sección “Credenciales”:

Le damos a “CREAR CREDENCIALES” de nuevo:

Seleccionamos “ID de cliente OAuth”:

En tipo de aplicación selccionamos “App de escritorio”, le damos un nombre y clickamos sobre “CREAR”:

Nos mostrará la siguiente información:

Podemos ver el cliente OAuth:

Seguimos con la consola de PlexDrive:

1. Please go to https://console.developers.google.com/
2. Create a new project
3. Go to library and activate the Google Drive API
4. Go to credentials and create an OAuth client ID
5. Set the application type to 'other'
6. Specify some name and click create
7. Enter your generated client ID: XXXXXXXXXXXXXXXXXXXXXXX
8. Enter your generated client secret: YYYYYYYYYYYYYYYYYYY
Go to the following link in your browser https://accounts.google.com/o/oauth2/auth?access_type=offline&client_id=ZZZZZZZZZZZZ-270hoqea0pinv0nc3sbpfkuja68alask.apps.googleusercontent.com&redirect_uri=urn%3Aietf%3Awg%3Aoauth%3A2.0%3Aoob&response_type=code&scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fdrive&state=state-token

Accedemos a la URL que nos indica PlexDrive mediante el usuario anteriormente autorizado:

Aparecerá el siguiente mensaje donde le damos a continuar:

Le volvemos a dar a continuar:

Nos mostrará un código:

En la consola pegamos dicho código:

Paste the authorization code: XXXXXXXXXXXXXXXXXXXX

Le damos a Ctrl+C y demonizamos el montaje de la unidad de GDrive:

vi /usr/local/etc/rc.d/gdrive

#! /bin/sh
#
# $FreeBSD$
#

# PROVIDE: gdrive
# REQUIRE: DAEMON
# KEYWORD: shutdown 

. /etc/rc.subr

name="gdrive"
rcvar="${name}_enable"
extra_commands="status"

start_cmd="${name}_start"
stop_cmd="${name}_stop"
status_cmd="${name}_status"

gdrive_start(){
    echo "Clearing cache files: ${name}"
    rm /root/.plexdrive/cache.bolt
    echo "Starting service: ${name}"
    /usr/sbin/daemon -S -p /var/run/${name}.pid -T gdrive -u root /root/plexdrive-freebsd-amd64 mount -o allow_other -v 3 --drive-id=0ANN6pzUlNUgXUk9PVA /mnt/gdrive
}

gdrive_stop(){
    if [ -f /var/run/${name}.pid ]; then
        echo "Stopping service: ${name}"
        kill -s INT $(cat /var/run/${name}.pid)
        sleep 3
    else
        echo "It appears ${name} is not running."
    fi
}

gdrive_status(){
    if [ -f /var/run/${name}.pid ]; then
        echo "${name} running with PID: $(cat /var/run/${name}.pid)"
    else
        echo "It appears ${name} is not running."
    fi
}


load_rc_config ${name}
run_rc_command "$1"

Asignamos los permisos necesarios:

chmod 555 /usr/local/etc/rc.d/gdrive
chown root:wheel /usr/local/etc/rc.d/gdrive

Habilitamos el servicio y lo arrancamos:

sysrc gdrive_enable="yes"
service gdrive start

Ahora importaremos el directorio /mnt/gdrive del padre a la jail.

Creamos el directorio en la jail:

iocage console GDrive
mkdir /mnt/gdrive
exit

Importamos el directorio mediante el comando fstab de IOCage:

iocage fstab -a GDrive “/mnt/gdrive /mnt/gdrive nullfs rw 0 0”

Successfully added mount to GDrive's fstab

Nginx:

Ahora accedemos a la jail y montamos el servidor, el acceso desde el exterior será por HTTPS ya que requeriremos de autenticación para acceder al contenido y no queremos que dichas credenciales se vean comprometidas, en cambio desde la LAN también sería conveniente utilizar HTTPS pero para ello deberíamos de utilizar el nombre de dominio público desde los equipos LAN lo cual da problemas por el Hairpining , para resolver esto hay tres opciones:

  • Montar un servidor DNS interno con RPZ y configurar los clientes internos para que utilicen dicho DNS.
  • Modificar el fichero /etc//hosts de los clientes para que el dominio resuelva a la ip LAN, pero en mi caso no es posible por tratarse de un AndroidTV.
  • Utilizar HTTP(opción por la que me decanté).

Instalamos Nginx y Socat ya que es una dependencia de ACME, la herramienta con la que emitiremos el certificado HTTPS:

pkg install nginx socat

Instalamos ACME:

Emitimos el certificado:

/root/.acme.sh/acme.sh –issue -d gdrive.alfaexploit.com -w /usr/local/www/nginx –renew-hook ‘service nginx restart’

La configuración de Nginx es la vanilla pero con un include adicional:

egrep -v ‘^($|[[:space:]]*#|;)’ /usr/local/etc/nginx/nginx.conf

worker_processes  1;
events {
    worker_connections  1024;
}
http {
    include       gdrive.alfaexploit.conf;
    include       mime.types;
    default_type  application/octet-stream;
    sendfile        on;
    keepalive_timeout  65;
    server {
        listen       80;
        server_name gdrive.alfaexploit.com;
        location / {
            root   /usr/local/www/nginx;
            index  index.html index.htm;
        }
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   /usr/local/www/nginx-dist;
        }
    }
}

Nuestra configuración específica:

vi /usr/local/etc/nginx/gdrive.alfaexploit.conf

server {
    listen 443 ssl default_server proxy_protocol;
    server_name gdrive.alfaexploit.com;

    set_real_ip_from 192.168.69.11;
    real_ip_header proxy_protocol;

    root   /mnt/gdrive;

    location / {
	autoindex on;
	auth_basic "Login Required";
	auth_basic_user_file /usr/local/etc/nginx/.htpasswd;
    }

    ssl_certificate "/root/.acme.sh/gdrive.alfaexploit.com/fullchain.cer";
    ssl_certificate_key "/root/.acme.sh/gdrive.alfaexploit.com/gdrive.alfaexploit.com.key";
}

# Direct IoT devices wich cant change /etc/hosts and evade hairpinning problem
server {
    listen       80 default_server;
    server_name  _;

    root   /mnt/gdrive;

    location / {
        autoindex on;
        auth_basic "Login Required";
        auth_basic_user_file /usr/local/etc/nginx/.htpasswd;
    }
}

# LetsEncrypt cert renew
server {
    listen       81 default_server;
    server_name  _;

    root   /usr/local/www/nginx;
}

Tenemos Nginx en claro en el puerto 80/81, el primero es para los dispositivos IoT en la LAN como TVs que no pueden cambiar el fichero /etc/hosts y por lo tanto el hairpinning da problemas, estos acceden directamente al 80. Por otro lado si lo dejamos así LetsEncrypt no podrá renovar el certificado ya que el docroot del puerto 80 es RO, en el HaProxy enviaremos el tráfico de LetsEncrypt al puerto 81 que está asociado a un docroot RW.

Generamos el fichero de credenciales:

openssl passwd -apr1

El resultado final debe ser muy similar a este:

cat /usr/local/etc/nginx/.htpasswd

kr0m:$apr1$gypxo60s$6vKdmpNM3J8gJ7pdcLkeg/

Reiniciamos el servicio:

service nginx restart


HaProxy:

En mi caso el tráfico es balanceado por un HaProxy donde se enviará el tráfico por HTTP en caso de tratarse de ACME renovando el certificado o por HTTPS en caso contrario, cuando sea por HTTPS se utilizará ProxyProtocol para asignar la ip origen de las peticiones:

cat /usr/local/etc/haproxy.conf
frontend HTTP
    bind :80
    option forwardfor

    # Allow http access only for LetsEncrypt:
    acl letsencrypt path_beg /.well-known/acme-challenge/
    http-request redirect scheme https unless letsencrypt

    acl gdrive hdr(host) -i gdrive.alfaexploit.com
    http-request deny if !gdrive
    use_backend gdrive if gdrive

frontend HTTP-SSL
    bind :443
    mode tcp

    acl gdrive_ssl req.ssl_sni -i gdrive.alfaexploit.com

    tcp-request inspect-delay 2s
    tcp-request content reject if !gdrive_ssl
    use_backend gdrive_ssl if gdrive_ssl

# GDrive is a special case:
# Nginx:80 -> Direct IoT devices wich cant change /etc/hosts and evade hairpinning problem docroot: /mnt/gdrive RO
# Nginx:81 -> Letsencrypt docroot: /usr/local/www/nginx RW
backend gdrive
    server gdrive 192.168.69.14:81 check

backend gdrive_ssl
    mode tcp
    option ssl-hello-chk
    server gdrive 192.168.69.14:443 check sni req.ssl_sni send-proxy-v2

KODI:

Ahora toca añadir la fuente en KODI, accedemos a los parámetros de configuración:

Contenidos:

Videos:

Añadir videos:

Buscar:

Añadir sitio de red:

Añadimos nuestro servidor HTTP o HTTPS si accedemos desde fuera de la LAN, si accedemos desde fuera pondremos el nombre de dominio gdrive.alfaexploit.com si lo hacemos desde la LAN directamente la dirección ip del servidor Nginx, en todos los casos debemos indicar las credenciales definidas en el htaccess::

Ahora aparecerá en la lista de recursos:

Navegamos por el servidor HTTP y añadimos el directorio deseado:

Le damos a OK:

Configuramos como queremos “escrapear” el directorio y le damos a OK:

A partir de aquí ya podremos ver las películas en la sección correspondiente de KODI.


Token expirado:

Google genera los tokens de apps no publicadas en modo “Testing”:

  - A Google Cloud Platform project with an OAuth consent screen configured for an external user type and a publishing status of "Testing" is issued a refresh token expiring in 7 days.

Que traducido sería algo así:

Un proyecto de Google Cloud Platform con una pantalla de consentimiento de OAuth configurada para un tipo de usuario externo y un estado de publicación de "Prueba" recibe un token de actualización que vence en 7 días.

Esto provoca el siguiente error en PlexDrive cada 7 días:

Response: {
  "error": "invalid_grant",
  "error_description": "Token has been expired or revoked."
}

Publicando la app esta limitación desaparecería, pero para publicar una app se requieren muchos pasos adicionales como configuración de DNS con entradas específicas de Google y demás.

Una maner sencilla pero manual de resolverlo es entrando cada semana y regenarando el token del usuario, para ello eccedemos a la sección de credenciales del proyecto:
https://console.developers.google.com/apis/credentials

Le damos a editar al id del cliente anteriormente creado:

Le damos a “REESTABLECER SECRETO”:

Confirmamos que deseamos reestablecer el secreto:

Ahora debemos resetear PlexDrive desde cero, en mi caso primero paro la jail que sirve el contenido:

iocage stop GDrive

Paro el servicio:

service gdrive stop

Elimino la configuración de PlexDrive:

rm -rf /root/.plexdrive/

Arranco PlexDrive manualmente donde nos pedirá los datos de acceso de nuevo:

/root/plexdrive-freebsd-amd64 mount -o allow_other -v 3 –drive-id=0ANN6pzUlNUgXUk9PVA /mnt/gdrive

1. Please go to https://console.developers.google.com/
2. Create a new project
3. Go to library and activate the Google Drive API
4. Go to credentials and create an OAuth client ID
5. Set the application type to 'other'
6. Specify some name and click create
7. Enter your generated client ID: XXXXXXXXXXXXXXXXXXXXX
8. Enter your generated client secret: YYYYYYYYYYYYYYYYYYYY

Go to the following link in your browser https://accounts.google.com/o/oauth2/auth?access_type=offline&client_id=ZZZZZZZZZZZZ-270hoqea0pinv0nc3sbpfkuja68alask.apps.googleusercontent.com&redirect_uri=urn%3Aietf%3Awg%3Aoauth%3A2.0%3Aoob&response_type=code&scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fdrive&state=state-token
Paste the authorization code: 4/1AX4XfWgwVi61FfxtHN0WpBUh_b3UvGhXfWscjb_B-oNQyl4zVniUtdqbQQg

Cuando haya conectado correctamente lo paramos:

Ctrl+C

Arrancamos el servicio:

service gdrive start

Finalmente arrancamos la jail:

iocage start GDrive

Si te ha gustado el artículo puedes invitarme a un RedBull aquí