Varias veces busqué películas que no aparecían en ningún streaming y las encontré en YouTube. El algoritmo hizo el resto: una vez que empezás a ver ese tipo de contenido, YouTube te muestra más. Con el tiempo quedó claro que había una cantidad enorme de material repartido entre canales, playlists y cuentas de todo tipo, sin ningún orden. En una época en que casi todo el cine de valor requiere una suscripción, tener ese material disponible y sin que nadie lo catalogara parecía un desperdicio. La decisión de hacer un catálogo y no un reproductor fue deliberada: incrustar videos de YouTube en un sitio propio siempre genera problemas de derechos. Tampoco quería publicidad, y no solo por una cuestión de filosofía del software libre: usar la API de TMDb requiere que el proyecto sea no comercial.
Por qué YouTube no sirve como catálogo
El problema central es que YouTube no está pensado para esto. Los títulos aparecen en distintos idiomas, muchas veces sin el año, mezclados con trailers y escenas sueltas, y muchas desaparecen cuando cambian los derechos sin que nadie avise. Hay canales que suben las películas con el título en alemán aunque el video sea en inglés, o que ponen el año de una restauración y no el de la película original. Otros directamente no ponen ningún dato útil en el título y confían en que el algoritmo los encuentre igual. Además los videos desaparecen sin aviso: un canal que hoy tiene 300 películas mañana puede tener 200 porque alguien reclamó derechos de algunas, o porque el canal fue dado de baja, o simplemente porque el dueño lo borró. No hay manera de saber qué está disponible en un momento dado sin verificarlo uno por uno, y esa verificación no se puede hacer a mano cuando hablamos de decenas de miles de videos.
Cómo está construido el proyecto por dentro
El proyecto está construido enteramente en Python, sin frameworks ni dependencias externas más allá de la biblioteca estándar. La base de datos es SQLite, elegida por su simplicidad y porque permite servir el catálogo directamente desde el navegador usando sql.js, una compilación de SQLite a WebAssembly. Esto significa que no hay servidor, no hay backend y no hay base de datos remota: el archivo se descarga una vez y todas las consultas corren localmente en el navegador del usuario. El sitio se publica en GitHub Pages, el dominio está en Cloudflare y la base de datos se sirve desde Cloudflare R2. Internamente hay dos bases: una completa donde escriben todos los scripts del pipeline, de unos 140MB, y una liviana con solo las películas confirmadas, de unos 16MB, que es la que descarga el navegador.
La estrategia de cosecha: por qué los canales y no las películas
La API de YouTube tiene dos presupuestos separados: 10.000 unidades diarias para operaciones generales y 100 búsquedas diarias para el buscador. Buscar película por película consume una búsqueda por consulta, así que con ese límite se pueden rastrear apenas 100 títulos por día. Con un catálogo de decenas de miles de películas posibles eso no lleva a ningún lado, y la alternativa obvia fue invertir la dirección: en lugar de buscar películas, identificamos canales de YouTube dedicados al cine clásico y los cosechamos completos. Listar todos los videos de un canal usa el endpoint `playlistItems.list`, que cuesta 1 unidad por cada 50 videos, de modo que un canal con 800 películas se enumera completo por 16 unidades. Con el presupuesto diario disponible es posible procesar cientos de canales sin ninguna restricción. El resultado de cada cosecha se almacena en la base de datos con el título del video, su duración, la fecha de publicación y el estado de disponibilidad geográfica según la API.
Encontrar los canales correctos fue su propio proceso. Hay canales institucionales, como el oficial de Mosfilm, el estudio soviético más grande de la historia, que subió su catálogo completo con subtítulos en inglés. Hay canales de distribuidoras que obtienen licencias para territorios específicos, como Film&Clips y sus variantes (uno general, uno de blanco y negro, uno en inglés), o Artflix Películas Clásicas, con foco en cine negro y de autor europeo. Y después hay una cantidad sorprendente de coleccionistas particulares repartidos por todo el mundo: NipponKino, especializado en cine japonés clásico con subtítulos; MeduFiles y prisoner, con foco en cine europeo de autor; Davide Fiammenghi, que subió buena parte de la obra de Bergman disponible en inglés; Grandpa's Old Movies Chest, con cine americano de dominio público; Khris McLorean y Ruvindu Gamage, con mezclas de cine europeo y japonés. También hay canales específicamente latinoamericanos: La Corriente Películas tiene la colección más grande de cine clásico doblado al español latino disponible en YouTube, y hay varios canales dedicados al cine argentino de la época de oro. El catálogo actual cubre más de 25 canales activos, con perfiles muy distintos en términos de calidad y confiabilidad. Algunos tienen los títulos perfectamente documentados con año, director y sinopsis; otros suben las películas con nombres genéricos sin ningún dato adicional, lo que complica considerablemente el proceso de identificación.
El matcher: cómo cruzamos videos contra el catálogo de IMDb
Una vez que tenemos los videos cosechados, el trabajo real es identificar cuál corresponde a qué película. El matcher es un script Python que corre completamente offline, sin consumir cuota de API, cruzando los videos de la base de datos contra el catálogo de IMDb. IMDb publica sus datasets completos en TSV comprimido y los actualiza a diario; el pipeline los descarga, los filtra por rango de años y los carga en SQLite. El cruce entre videos y catálogo procesa todos los pares posibles en memoria, usando índices invertidos de tokens para reducir el espacio de búsqueda antes de calcular cualquier score.
El proceso empieza con una serie de vetos duros que descartan los casos imposibles antes de cualquier análisis. Cualquier video cuyo título contenga palabras como trailer, clip, reseña, compilación, maratón, soundtrack o reaction se elimina directamente. Lo mismo pasa con los videos que duran menos de 55 minutos, que por definición no son largometrajes, o con los que duran más de 1.6 veces el metraje esperado, que suelen ser maratones o recopilaciones. También se descartan los videos que declaran un año con más de un año de diferencia respecto de la película buscada.
Después del filtro inicial, la señal más confiable es la presencia del título de la película como texto contiguo en el título del video. Pero implementar esa señal correctamente fue más complejo de lo que parecía. El sistema normaliza los títulos antes de comparar: convierte números escritos en dígitos para que "Three Guys Named Mike" matchee con "3 Guys Named Mike", colapsa guiones internos para que variantes de escritura no generen falsos negativos, y equipara plural y singular simple para casos como "Outlaws of the Range" vs "Outlaw of the Range". Sin esa normalización, variantes perfectamente válidas del mismo título se perdían.
Los títulos alternativos en otros idiomas no pueden confirmar la señal de frase. Hay películas con AKAs en italiano, francés o alemán que podrían matchear con cualquier video que contenga esas palabras. Para los títulos de una sola palabra el riesgo es todavía mayor: "Romance", "She" o "Shark" aparecen en decenas de videos completamente distintos. En esos casos el sistema exige que el año de la película aparezca explícitamente en el título del video. Hay además un piso mínimo de similitud de título que impide que el año y la confianza del canal solos confirmen un match con un título débil. Sin ese piso, un video en un canal de alta confianza con el año correcto podía terminar confirmado aunque el título no tuviera ninguna relación.
Cuando la frase no es concluyente, el matcher calcula un score ponderado que combina similitud de título (50%), duración (33%), año (9%) y confianza del canal (8%). La similitud de título usa distancia de edición normalizada sobre los tokens relevantes, descartando palabras frecuentes como artículos y preposiciones. Para confirmar sin frase se requiere una similitud de al menos 88% con una duración dentro del 20% del metraje esperado. Todo esto se construyó con una suite de tests: cada fallo real que encontramos, como "La Strada" matcheando con cualquier video con "street" en el nombre, o "The Bridge of San Luis Rey" confirmando cualquier video con "bridge", se convirtió en un test que el sistema tiene que pasar antes de cualquier cambio.
La IA para los casos que el matcher no puede resolver
Los casos que el matcher no puede resolver solos van a una segunda etapa de revisión con IA. El sistema usa la API de Anthropic con Claude como modelo, y lo hace en tres escenarios distintos. El primero son los pendientes: videos donde el matcher calculó un score intermedio, no suficiente para confirmar ni para rechazar. El segundo son los "sin identificar": videos cuyo título en YouTube no corresponde al título del catálogo, ya sea porque están en otro idioma, porque usan un título alternativo poco conocido, o porque el canal directamente no puso el título real. El tercero es la auditoría periódica: después de agregar canales nuevos o de cambiar la lógica del matcher, se corre un script que revisa las confirmadas con score bajo y las somete de nuevo al modelo para detectar falsos positivos que el matcher aceptó con poca certeza. En cada caso el modelo recibe el título del video, su descripción completa cuando está disponible, la duración en minutos, y los datos de la película del catálogo: título original, títulos alternativos, año, director y duración según IMDb. El prompt le pide una decisión binaria con justificación. La descripción del video es particularmente útil en canales que la completan bien: algunos incluyen director, actores, estudio y año, lo que permite identificar películas cuyo título en el video no tiene ninguna relación con el título real. En una de las primeras pasadas de auditoría se eliminaron más de 200 falsos positivos que el matcher había aceptado.
Hay un segundo proceso de identificación con IA que corre de forma distinta: la búsqueda por directores. Cuando queremos incorporar la filmografía de un director específico, como Bergman, Fellini o Kurosawa, no alcanza con cosechar canales y esperar que el matcher los encuentre, porque muchas de esas películas están en canales con títulos en otros idiomas o con metadatos pobres. En ese caso corremos un pipeline de búsqueda activa que usa el buscador de YouTube para encontrar videos candidatos, los audita con IA en varias pasadas progresivamente más estrictas, reverifica los metadatos reales de los videos via API, y finalmente los somete a revisión humana antes de insertarlos. Fue en este proceso donde aparecieron los límites más claros de la auditoría automática: videos en ruso sin subtítulos que pasaban todos los filtros automáticos, videos que la API marcaba como disponibles pero que al abrirlos no cargaban desde Argentina, y videos que el modelo aprobaba pero que al verlos resultaban ser algo completamente distinto. La revisión humana al final del pipeline no es opcional.
El problema del geobloqueo y sus límites
Uno de los problemas más difíciles de resolver es el geobloqueo. La API de YouTube devuelve información sobre restricciones geográficas a través del campo `regionRestriction`, que indica qué países están bloqueados o cuáles son los únicos permitidos. Eso permite filtrar los casos obvios, como canales que solo tienen licencia para Estados Unidos. Pero hay dos tipos de restricción que la API no captura: las restricciones de licencia que los estudios aplican dinámicamente a través del sistema de Content ID, y los canales que simplemente no están disponibles en ciertos territorios sin que eso quede registrado en los metadatos. El resultado es que hay películas que aparecen como disponibles según la API pero que al intentar verlas desde Argentina muestran un error. Por eso el catálogo mantiene un campo de verificación con tres estados posibles: verificada por la API, verificada manualmente desde Argentina, o bloqueada. Para el flujo normal de cosecha de canales la verificación de disponibilidad la hace la IA como parte del proceso de auditoría. Para las películas incorporadas por el pipeline de directores, la verificación fue manual: cada video fue abierto y comprobado desde Argentina antes de agregarlo al catálogo.
Cómo se obtienen posters, sinopsis y países de producción
Una vez confirmada una película, el pipeline la enriquece con datos adicionales de tres fuentes distintas. Los posters y sinopsis vienen de TMDb, The Movie Database, una base de datos colaborativa de cine con API gratuita para proyectos no comerciales. El script cruza el identificador de IMDb de cada película contra la API de TMDb y descarga el poster en resolución estándar y la sinopsis, primero en español y con fallback al inglés si no existe traducción. Para las películas que TMDb no tiene o que tienen posters de mala calidad, hay scripts alternativos que buscan en Wikipedia.
El país de producción viene de Wikidata a través de consultas SPARQL. Wikidata tiene una propiedad específica para país de origen de películas (P495) y permite cruzar por el identificador de IMDb, así que el proceso es directo y no requiere API key ni tiene costo. El resultado permite filtrar en el catálogo por cine estadounidense, italiano, soviético, argentino, japonés, francés y varios otros, incluyendo coproducciones que aparecen bajo más de un país.
El enriquecimiento está diseñado para no repetir trabajo: cada script solo procesa las películas que todavía no tienen el dato correspondiente, de modo que se puede correr cada vez que se agregan películas nuevas sin volver a procesar todo el catálogo.
El sitio web: sin servidor, sin framework, sin backend
El sitio es HTML, CSS y JavaScript puro, sin frameworks ni dependencias de build. La decisión de no usar React, Vue ni ningún otro framework fue deliberada: el proyecto tiene que poder deployarse en GitHub Pages sin ningún proceso de compilación, y tiene que seguir funcionando en diez años sin que nadie lo mantenga. Toda la lógica de filtrado, búsqueda y paginación corre en el navegador usando sql.js para hacer queries SQL directamente sobre la base de datos descargada. Esto significa que el sitio funciona completamente offline una vez que la base de datos está cacheada, y que no hay ningún servidor que pueda caerse o que haya que escalar.
El catálogo permite filtrar por década, país, género, idioma de audio y estado de disponibilidad en Argentina. Los filtros se reflejan en el hash de la URL, lo que permite compartir búsquedas específicas como links directos. Hay un botón de "Sorprendeme" que abre una película al azar de las confirmadas, y cada película tiene su propia página estática con schema.org para ser indexada por Google. Esas páginas se generan con un script Python que lee la base de datos y produce un HTML por película, con metadatos, poster, sinopsis, datos técnicos y películas relacionadas por director y género.
Para los datos de usuario, el sitio usa Firebase Auth con Google Sign-In y Firestore para guardar favoritos e historial. Es la única dependencia externa que tiene el sitio en producción: el pipeline de construcción del catálogo usa varias APIs (YouTube Data API, Anthropic y TMDb), pero una vez publicado el sitio corre sin ninguna llamada a servicios externos salvo Firebase.
El estado actual del catálogo y lo que viene
El catálogo tiene hoy casi 4.000 películas confirmadas, cubre el período 1920 a 1979, e incluye cine de más de 25 países. El pipeline sigue activo: se agregan canales nuevos cuando aparecen, se buscan directores que todavía no están cubiertos, y se corre mantenimiento periódico para detectar videos caídos y buscar reemplazos. El código está disponible en GitHub bajo una licencia libre. Todo lo que está en el catálogo se puede ver gratis, sin registro y sin publicidad, desde Argentina.
