Me gustan las películas, como a cualquiera, y he acumulado muchas a lo largo de los años, aunque no siempre las he obtenido de las maneras más formales posibles. 😉
Para organizarlas y verlas suelo usar Plex y me gusta mucho, es una gran plataforma y merece un par de billetes de mi parte en mi opinión, pero ya que últimamente han estado tomando decisiones como mínimo curiosas volví a usar con más frecuencia una instancia de Emby que uso para fines similares.
Todo estaba yendo relativamente bien hasta que empezó a ir relativamente mal: me di cuenta de que, a veces, las películas o videos que guardo ahí tenían dificultades para comenzar a reproducirse y/o para continuar la reproducción cuando hacía saltos en la línea de tiempo. No era un problema enorme, ya que no es común que se hagan saltos temporales cuando se ve una película, pero el problema ya había captado mi atención.
Pensé primero que mis discos duros estaban fallando, ya que este problema no sucedía hace un par de años cuando mudé mi biblioteca multimedia a ellos, así que realicé un par de análisis SMART y parecía estar todo bien con ellos. Descartado.
Creí que podía ser un caso de cuello de botella de red, pero era muy improbable dado que el problema lo observé mientras se realizaba una reproducción local de los archivos y, además, recientemente había mejorado el ancho de banda de mi red local a unos nada despreciables 10 Gigabits (más de esto en algún otro post en el futuro). Descartado.
Se me pasó por la mente que alguno de los nodos (computadoras/servidores) podría estar teniendo problemas en el momento de saltar de un lado al otro de la película, pero no vi en ningún caso un uso excesivo del CPU. Descartado.
Descarté un par de opciones más durante días y semanas, pero me di cuenta de algo interesante mientras intentaba repetir adrede el error con fines de diagnóstico. No importaba de cuántos minutos era el salto, no importaba si adelantaba 5 o 50 minutos, de todas formas había una demora notable; pero más interesante aún, me di cuenta de que si no pasaba el cursor por encima de la línea de tiempo y saltaba directamente poniéndolo justo en el punto al que quería saltar el retraso prácticamente desaparecía. Muy interesante.
El problema
Revisando los registros en la ventana de DevTools del navegador, noté que había muchas solicitudes hechas al mismo endpoint, todas comenzaban por Thumbnail y entonces todo cobró sentido: mientras hacía scrubbing, o sea, mientras pasaba el cursor por la línea de tiempo para ver una pequeña previsualización de lo que pasa a lo largo de ella, se hacían decenas de solicitudes que permitían a Emby obtener las miniaturas para la previsualización. Miniaturas de poco peso, sí, pero que en el orden de decenas van retrasando de manera significativa que subsecuentes acciones se lleven a cabo.

No logré determinar cuántas solicitudes se realizan por cada ¿píxel?, ¿quizás por cada em?, pero resultan muchas en poco tiempo haciendo scrubbing y puede llegar a ser frustrante porque el video vuelve a reproducirse sólo cuando todas ellas (o por lo menos un buen porcentaje) han sido completadas, satisfactoriamente o no.
La solución
Visitando los foros de Emby vi que recomendaban activar la opción de guardar las miniaturas en el mismo disco donde reside el contenido. Es una opción que yo había deliberadamente dejado desactivada porque no quería usar espacio de forma innecesaria. Añadido a esto, se recomendaba también que cada archivo de video resida dentro de su propio directorio y no tenerlos a todos dentro del mismo nivel debido a que así tanto los sistemas operativos involucrados (Debian para el NAS y Windows para el cliente que estaba usando en ese momento) como Emby mismo tendrían menos trabajo encontrando y accediendo a archivos.
Manos a la obra, entonces.

Esto inició un proceso que haciendo uso de, hm hm, FFmpeg genera archivos BIF (creado por Roku) que encapsulan instantáneas tomadas a intervalos regulares de la totalidad del video. Hasta ahí todo bien, pero el proceso toma aproximadamente 9 segundos por gigabyte en mi hardware y con varios terabytes a cuestas iba a tomar unas buenas horas.
Un tiempo después, y con el proceso terminado, tenía el doble de archivos en mi directorio principal de películas/videos, cada archivo MP4 o MKV tenía un análogo en formato BIF con el mismo nombre. Si hago un poco de memoria, recomendaban por ahí no tener tantos archivos al mismo nivel y lo que hice duplicó el número original, todo mal. Lo bueno es que el siguiente paso seguramente mejoraría las cosas.
Tenía que pasar cada dupla de archivos a su propia carpeta, lo malo es que Emby no se pone nada contento cuando le mueven los archivos de lugar. No importa si sólo los pones dentro de otra carpeta y ya, representa un cambio en su ubicación y eso basta. La consecuencia de esto es que Emby no reconocería los “nuevos” archivos como archivos de los cuales ya tenía conocimiento y las referencias a los “antiguos” quedarían invalidadas, estropeando así listas de reproducción y favoritos, por ejemplo.
Leyendo por ahí, recomendaban el uso de los archivos NFO porque podrían preservar información importante sobre los archivos y siempre y cuando estén junto a los videos esa información estará disponible.
Hice la prueba, activé la opción de guardar todo en el disco multimedia en formato NFO y mover un par de películas a sus respectivas nuevas carpetas. Reescaneé la carpeta en Emby, refresqué los metadatos pero nada, las películas en cuestión habían desaparecido de las listas de reproducción en donde se encontraban así como de favoritos. Lo curioso es que si regresaba los archivos a su ubicación original todo volvía a la normalidad. Al parecer, la forma correcta de quitar ítems de estas listas es deliberadamente quitándolos mediante las aplicaciones o la API.


Claramente necesitaba otra forma de preservar mis favoritos y listas. Noté también que Emby parece darle un identificador único a cada video pero basado en un índice que va incrementándose conforme se van añadiendo nuevos archivos, lo cual significa que, al Emby considerar los archivos movidos como nuevos, tampoco conservaban sus IDs y obtenían uno nuevo.
Lo mejor que se me ocurrió fue resolverlo por medio de los nombres de las películas/videos. Estaba casi 100% seguro de que ninguno se repetiría y que, básicamente, funcionarían como un identificador único, pero Emby no piensa igual, así que habría que usar su API para hacer una búsqueda por el nombre y así poder llegar al verdadero ID (al nuevo) para poder volver a agregar los archivos a favoritos.
Algo a tener en cuenta es que cuando se hace la búsqueda mediante la API o GUI Emby no devuelve un solo resultado aunque haya una coincidencia exacta, así que tenemos que refinar la búsqueda. Una simple comparación de cadenas debería ser suficiente.
import requests
import json
EMBY_IP = ""
API_KEY = ""
USER_ID = "" # In the format XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
PARENT_ID = "" # In case the videos need to be filtered by library
HEADERS = {"X-Emby-Token": API_KEY}
def get_favorites():
url = f"{EMBY_IP}/Users/{USER_ID}/Items?Recursive=true&IsFavorite=true&ParentId={PARENT_ID}&IncludeItemTypes=Movie"
response = requests.get(url, headers=HEADERS)
if response.status_code == 200:
items = response.json().get("Items", [])
return [item["Name"] for item in items]
else:
print("Error fetching favorites:", response.text)
return {}
def search_new_item_id(name):
url = f"{EMBY_IP}/emby/Items?Recursive=true&SearchTerm={name}"
response = requests.get(url, headers=HEADERS)
if response.status_code == 200:
items = response.json().get("Items", [])
matching_items = [item for item in items if item["Name"].strip().lower() == name.strip().lower()]
if matching_items:
return matching_items[0]["Id"]
return None
def re_add_favorites(old_favorites):
for name in old_favorites:
new_id = search_new_item_id(name)
if new_id:
url = f"{EMBY_IP}/Users/{USER_ID}/FavoriteItems/{new_id}"
response = requests.post(url, headers=HEADERS)
if response.status_code == 200:
print(f"Re-added favorite: {name}")
else:
print(f"Failed to re-add {name}: {response.text}")
else:
print(f"Could not find new ID for: {name}")
if __name__ == "__main__":
print("Fetching current favorites...")
old_favorites = get_favorites()
input("Move your files now and press Enter when done...")
print("Re-adding favorites with new paths...")
re_add_favorites(old_favorites)
Pero ahora los archivos están en tríos, ya que tenemos el archivo multimedia, el archivo de miniaturas (BIF) y el archivo de metadatos (NFO), y moverlos manualmente a sus respectivas nuevas carpetas puede ser engorroso. Es engorroso.
Para resolverlo usé FileBot, que es aparentemente bastante usado por usuarios en los foros de Emby y Plex para organizar archivos, mover, copiar, eliminar y más cosas. Lo malo es que tiene costo, lo peor es que es una suscripción, pero considerando que no es muy caro (tiene un precio de 6 dólares anuales al momento de escribir este artículo) y que muy probablemente tendré que realizar este tipo de operaciones con cierta frecuencia en el futuro, sumado a que tenía flojera de escribir otro script, decidí darle una oportunidad a una herramienta probada por la comunidad.


Usando el preset “File to Folder Name” logramos que se cree una carpeta común para los tres archivos y que ese sea su nuevo hogar. En el ejemplo no hay archivos de otro tipo pero siempre que todos tengan exactamente el mismo nombre (sin contar la extensión, claro) serán puestos en el mismo directorio.
Para ir terminando
Con los cambios anteriores el rendimiento es observable en algunos aspectos y significativo en otros. Por ejemplo, cuando se trata de escanear las bibliotecas de videos, aparentemente se realizan un poco más rápido; cuando se trata del scrubbing, la aparición de miniaturas sobre la línea de tiempo es casi instantánea y ya casi no hay tiempo de espera al saltar a otro punto sin importar de qué forma se haga. Parecen resultados satisfactorios.
Para algún día
- Escribir un script para realizar el mismo proceso pero con listas de reproducción. No debería ser complicado, es bastante parecido al código que ya tenemos, se podría adaptar sin muchos problemas.
- Si bien puedo usar FileBot en cuantas máquinas necesite casi sin importar la plataforma (hay soporte para Docker, Ubuntu, macOS, Windows y hasta CentOS), sería prudente reemplazarlo con un script básico. Por el momento, ya hice el gasto y haré uso del programa para estos menesteres, pero para lo que necesito unas cuantas líneas de código deberían bastar.