1 puntos por GN⁺ 2025-05-24 | 1 comentarios | Compartir por WhatsApp
  • El proyecto surgió del problema de que incluso en 2025 sigue siendo difícil escuchar y sincronizar libremente en iPhone los archivos de música que el usuario ya posee, así que implementó por su cuenta un reproductor de música local-first en SwiftUI
  • Si se cancela la suscripción a Apple Music, se detiene la sincronización de iCloud Music Library, y iTunes Match queda como una opción de pago aparte por $24.99 al año para guardar en línea copias AAC de 256 kbps
  • React Native/Expo fallaba con frecuencia o se cerraba al recorrer carpetas de iCloud profundamente anidadas y manejar permisos de archivos, por lo que una implementación en SwiftUI con APIs nativas de Apple terminó siendo una opción más simple
  • La app usa SQLite FTS5 para buscar por nombre de archivo y metadatos como artist, album y title, y separa la importación desde iCloud, la gestión de la biblioteca y la pantalla de reproducción
  • En iOS siguen existiendo barreras de acceso a archivos y distribución debido a los bookmarks con alcance de seguridad y a las limitaciones de las builds personales para desarrolladores

Por qué hice mi propio reproductor de música

  • Sigue existiendo la situación de que, para reproducir en iPhone archivos de música que el usuario ya posee, hay que pagarle a Apple o sortear varias restricciones
  • Para quienes ya tienen organizada su biblioteca de música local, los servicios de streaming se parecen más a una función de conveniencia que a una necesidad
  • Si se cancela la suscripción a Apple Music, se interrumpe la sincronización entre dispositivos basada en iCloud Music Library
    • Esta función está detrás de la suscripción
    • Con iTunes Match se puede volver a usar por $24.99 al año
    • Match guarda en línea copias AAC de 256 kbps, mientras que los archivos originales permanecen igual a menos que el usuario los cambie
    • Sin suscripción, desaparece la sincronización en la nube y hay que volver a la sincronización por cable/Wi‑Fi
  • El objetivo era crear directamente un reproductor de música básico que importara, organizara y reprodujera archivos de audio
  • La app terminada ofrece búsqueda de texto completo, soporte para iCloud y una experiencia local-first, y está publicada en GitHub

Por qué las opciones existentes no encajaban

  • La app predeterminada de Apple

    • La app Files puede reproducir directamente archivos de música de iCloud, pero no fue diseñada como una app para escuchar música
    • Le faltan funciones clave como gestión de playlists, ordenamiento por metadatos y cola de reproducción
    • Aunque reproducir música sí es posible, la experiencia general de usuario es limitada, y hay discusiones relacionadas en la comunidad de Apple
  • Apps de terceros

    • En App Store hay muchas apps para reproducir música offline, pero incluso las que reproducen archivos que el usuario ya posee suelen aplicar precios basados en suscripción
    • Doppler tenía la ventaja de usar un modelo de pago único
    • Aun así, su UX estaba centrada en la gestión por álbumes, la calidad de búsqueda no resultaba satisfactoria, y la importación desde iCloud con muchas carpetas anidadas era lenta y difícil de usar

El proceso de pasar de React Native a SwiftUI

  • La elección inicial: React Native/Expo

    • Por una experiencia pasada con Swift, decidió evitarlo al principio y elegir React Native/Expo, que le permitía reutilizar su experiencia en desarrollo web
    • Implementar la UI de reproducción fue fácil, y usó la plantilla music-player de Gionatha Sturba, que parecía tener las funciones necesarias
    • El problema creció en el acceso al sistema de archivos y la sincronización de archivos en la nube
    • Librerías como expo-filesystem soportaban la selección básica de archivos, pero el recorrido recursivo de directorios de iCloud profundamente anidados fallaba con frecuencia o hacía que la app se cerrara
    • El sandbox de iOS impide que una app lea archivos sin permisos explícitos del usuario
    • Como resultado, el enfoque basado en JavaScript se volvió más complejo que usar directamente las APIs nativas de Apple, y al cambiar a Swift podía controlar mejor el acceso a archivos de iCloud y los permisos del sandbox
  • Por qué eligió SwiftUI

    • Eligió SwiftUI en vez de UIKit o storyboards para construir una capa de UI declarativa y concentrarse en la lógica de dominio y la sincronización de datos
    • La integración de async/await y los Swift Actors facilitó el manejo del flujo de datos y la concurrencia
    • SwiftUI ayudó a estructurar la app en componentes independientes de ViewModel
    • LLMs como OpenAI o1 y DeepSeek tendían a generar código puro de UI o de data binding con menos interdependencias complejas

Estructura de la app y modelo de datos

  • Tres pantallas principales

    • La app se divide en tres flujos: Library import, Library management y Player and playback
    • Library import agrega carpetas de la biblioteca en iCloud, escanea archivos de audio en todas las carpetas e inserta las rutas en una base de datos SQLite
    • Library management administra las canciones agregadas y organiza playlists
    • Player and playback se encarga de repetir, shuffle, gestión de cola, reproducir, pausar y pasar a la siguiente canción
    • El selector de archivos predeterminado de Apple no encaja con un flujo donde se quieren elegir de una sola vez varios directorios y múltiples archivos buscándolos por palabra clave
    • Si la app se ejecuta con la biblioteca vacía, en la pestaña Sync se muestra el botón “Add iCloud Source”
    • Al elegir una carpeta, se muestra una barra de progreso durante el recorrido del árbol, y cuando termina la indexación se pasa a la pestaña Library
    • En la primera pantalla se muestran Playlists / Artists / Albums / Songs
    • Al tocar una pista aparece un Mini-Player en la parte inferior, y al tocarlo se abre el Player en pantalla completa
    • El Player en pantalla completa ofrece shuffle, repetición, reordenamiento de cola y volumen
    • Si en Sync se pulsa “+” para elegir otra carpeta, las canciones nuevas se fusionan en segundo plano sin necesidad de reiniciar
  • Capa de lógica tipo servidor

    • Aunque es una app móvil, aplicó una arquitectura estilo backend basada en su experiencia con backends web y cloud
    • Separó la capa de dominio y lógica de las capas View y ViewModel para manejar con estabilidad la sincronización en la nube, el parseo de metadatos y el acceso a datos en SQLite
    • La estructura por capas es la siguiente
      • SQLite guarda las filas originales de canciones y el índice FTS
      • El Repository envuelve la base de datos y expone una API asíncrona
      • El Actor de dominio posee las reglas de negocio como importación, búsqueda y lógica de cola
      • El ViewModel se suscribe al Actor y transforma los datos en structs para la UI
      • Las Views de SwiftUI renderizan los datos recibidos
    • Los cambios de estado se mantienen de forma thread-safe, y se separan iCloud sync, reproducción y UI para que no crucen capas directamente

Búsqueda local hecha con SQLite FTS5

  • El almacenamiento persistente usa SQLite y no CoreData
  • Evitó CoreData porque quería controlar directamente el esquema, las consultas en crudo y sobre todo la búsqueda de texto completo
  • Desde alrededor de iOS 11 se puede usar SQLite con funciones FTS sin configuración adicional
  • Para consultas generales usó SQLite.swift, pero las consultas FTS se manejaron con sentencias SQL normales
  • SQLite FTS5 permite consultar metadatos como nombre de archivo, artist, album y title sin infraestructura de búsqueda separada
  • Configuración de las tablas FTS

    • Usa dos tablas FTS5
    • songs_fts: la usa SQLiteSongRepository e indexa artist, title, album, albumArtist
    • source_paths_fts: la usa SQLiteSourcePathSearchRepository e indexa fullPath, fileName
    • Ambas tablas FTS están junto a las tablas base tipo B-tree songs y source_paths
    • La FTS es solo lectura desde la UI, y todas las escrituras se manejan dentro del Repository
    • En el ejemplo de creación de songs_fts, songId se deja como UNINDEXED y se usa el tokenizer unicode61
    • Eligió unicode61 para manejar varios tipos de caracteres
    • Las claves que no se buscan se marcan como UNINDEXED para evitar que el diccionario de términos crezca innecesariamente
  • Búsqueda parcial y ranking

    • Cuando el usuario escribe “lumine”, internamente se añade un wildcard como “lumine*” para encontrar de inmediato resultados de búsqueda parcial
    • Para ordenar los resultados se usa bm25 de SQLite
    • Este enfoque ofrece un esquema predecible, un acceso local-first y una búsqueda de texto completo potente sin agregar dependencia de red ni servicios externos

Acceso a archivos en iOS y limitaciones de bookmarks

  • Las apps de iOS pueden guardar bookmarks persistentes para recordar ubicaciones de archivos
  • Pero los security-scoped bookmarks que otorgan acceso extendido a archivos fuera del sandbox de la app solo están disponibles en macOS
  • Las apps de iOS pueden recordar rutas de archivos con bookmarks normales y volver a pedir acceso mediante el document picker, pero no hay garantía de que ese acceso siga activo silenciosamente
  • Apple lo explica en su documentación sobre bookmarks
  • Para mitigarlo, implementó un mecanismo alternativo que copia los archivos al propio contenedor sandbox de la app
    • Mientras el bookmark sigue siendo válido, copia preventivamente los archivos en segundo plano
    • Así reduce el riesgo de intentar acceder a referencias inválidas de archivos de audio si iOS restablece los permisos
  • Este enfoque también mejora la velocidad de indexación
    • Escanea una vez la estructura de carpetas mientras el acceso está activo
    • Importa solo los archivos de audio relevantes
    • Recorre de forma segura directorios profundamente anidados
  • El problema de reproducir de forma estable archivos de audio individuales ubicados fuera de la app incluso después de reiniciar el dispositivo sigue sin resolverse
  • Incluso en apps nativas, este caso de uso no está bien soportado en iOS, y manejar de forma confiable el acceso a archivos sigue siendo complicado

Implementación de reproducción y UI

  • Parseo de metadatos

    • Para parsear metadatos de archivos de audio usó el framework AVFoundation de Apple y AVURLAsset
    • AVURLAsset permite inspeccionar metadatos de archivos multimedia como title y album artist
    • Algunos campos, como el número de pista, tuvo que buscarlos directamente en etiquetas ID3
    • Como la documentación oficial no cubría suficientemente los casos límite, recurrió a ejemplos de implementación encontrados buscando en GitHub
  • Reproducción de audio

    • Después de indexar la biblioteca, implementó el reproductor inicializando instancias de AVAudioPlayer para reproducir
    • Para controlar la música desde Control Center tuvo que implementar el protocolo AVAudioPlayerDelegate y conectarlo con MPRemoteCommandCenter de Apple
    • MPRemoteCommandCenter permite que la app responda a controles de reproducción a nivel del sistema

La experiencia con Swift y el desarrollo para Apple

  • Lo difícil

    • Las previews en tiempo real de SwiftUI en Xcode son un avance, pero la experiencia general de desarrollo no alcanza el nivel de integración con VSCode, recarga en tiempo real del simulador y herramientas de depuración familiares que Flutter ya ofrecía hace 5 años
    • El soporte LSP de Swift para Neovim o VSCode requiere herramientas adicionales como xcode-build-server y no se siente al nivel de la experiencia de desarrollo del ecosistema centrado en web
    • Algunas partes del SDK de Apple todavía mantienen estilo Objective-C
    • La búsqueda de archivos con Spotlight solo se expone a través de NSMetadataQuery, y NSMetadataQuery usa KVO y claves de texto
    • Todavía no existe un wrapper amigable para Swift
    • La UI declarativa de SwiftUI es útil, pero el comportamiento completo de la app con permisos de iCloud involucrados no puede emularse en previews, así que hubo que hacer mocks directos de las interacciones con la nube
  • Lo bueno

    • Gracias a async/await, pudo escribir código de concurrencia orientado a I/O como si fuera código imperativo, sin callbacks
    • También fue fácil meter código síncrono dentro de un Actor y llamarlo como en el ecosistema de JavaScript
    • Pudo usar directamente librerías nativas de Apple sin quedar limitado por bindings open source de React Native o Flutter
    • Muchas APIs de Apple tienen ejemplos, lo que hizo más fácil empezar
    • El estilo de composición de UI tipo React de SwiftUI ofreció productividad y espacio para explorar

iOS dificulta usar por mucho tiempo una app personal

  • Tras unas 1.5 semanas de implementación, logró crear un reproductor de música local/offline capaz de importar archivos de audio desde almacenamiento en iCloud
  • Pero sin un certificado de desarrollador, la app solo funciona 7 días y después hay que volver a compilarla
  • Para inscribirse en el Apple Developer Program hay que pagar $99
  • Incluso después de la DMA Act en la UE, el sideloading no está completamente abierto
    • Los usuarios de la UE pueden instalar apps de marketplaces de terceros desde sitios de desarrolladores
    • Los desarrolladores aún deben inscribirse en el programa de Apple de $99 al año
    • También deben aceptar los Alternative Terms de Apple
    • Para uso personal o como hobby, el límite de 7 días en builds de desarrollo no desaparece
  • Las PWAs en iOS también siguen teniendo limitaciones
    • Incluso después de las actualizaciones de iOS 16~18.x, las PWAs siguen ejecutándose dentro del sandbox de Safari
    • WebGL2 y web-push sí están disponibles
    • No se ofrecen Web Bluetooth, USB, NFC, Background Sync ni almacenamiento garantizado por encima de unos 50 MB
    • Como WebGL pasa por un shim de Metal, el framerate real suele quedar por debajo del de una app nativa en Metal
  • La IA ha reducido la complejidad del desarrollo de software al facilitar el acceso al conocimiento necesario para trabajar con tecnologías desconocidas, pero las apps de iOS todavía deben ajustarse a reglas artificiales
  • Incluso para una app hecha por una sola persona, sigue existiendo la barrera final de Apple si se quiere ejecutar por más de una semana, lo que limita el desarrollo y la distribución de apps personales

1 comentarios

 
GN⁺ 2025-05-24
Opiniones en Hacker News
  • Viví la época en la que Winamp era el reproductor de música predeterminado, y aun en la era del streaming sigo teniendo mi biblioteca de música local organizada en carpetas.
    Como otras personas en estos comentarios, hice como proyecto de hobby un reproductor de música a la antigua para escuchar música sin conexión; es una app HTML/JS de una sola página, con controles completos por teclado y una función simple de cola.
    Pueden probarlo aquí: https://nobsutils.com/mp

    • Incluso ahora, unos 27 años después, sigo pensando que la UI de Winamp estaba cerca de ser la respuesta correcta.
      Me gustaba esa simplicidad: un conjunto de archivos dentro de un directorio, shuffle de toda la colección, reproducir solo un directorio específico.
    • Me gusta porque estaba buscando un reproductor de música así de simple.
      Como dato, cmd + d, que hoy es el atajo estándar para agregar marcadores, cambia el tema en vez de guardar el sitio en favoritos.
    • Para mí, el reproductor de música predeterminado siempre fue foobar2000, y ahora lo reemplacé por la app Cog.
  • Durante 25 años armé una colección de música en formato FLAC, y el año pasado compré un teléfono Android y una tarjeta MicroSD de 1 TB, así que por fin pude meter toda mi música.
    Fue un proyecto largo hasta que la tecnología se puso al día, pero ahora estoy bastante satisfecho de poder llevarlo todo en el bolsillo.
    No puedo ser la única persona que no quiere convertirse en inquilina, renunciando al control mientras hace streaming de lo que la industria le empuja y se aguanta los anuncios; también es genial ver a gente que incluso crea sus propias apps.

    • La tecnología ya se había puesto al día hace mucho; más bien parece que te empeñaste en usar un formato que no era adecuado para ese propósito.
      Si re-encodeas bien, puedes meter toda tu música en una tarjeta mucho más pequeña con una calidad transparente, en la que no se puede oír la diferencia, y conservar los FLAC en el escritorio como respaldo.
    • Tu colección está realmente muy bien curada.
      En mi colección, aunque FLAC/APE/ALAC/WavePack son solo alrededor del 25%, ya supera los 3 TB, así que me cuesta escuchar música mientras me desplazo.
      No puedo decidir de antemano qué poner en un dispositivo móvil.
    • Yo también estoy armando una colección personal solo en FLAC, aunque desde hace mucho menos de 25 años, y ya superó 1 TB.
      La combinación de https://www.navidrome.org como servidor y https://symfonium.app como cliente me funcionó muy bien.
      Ahora también existen tarjetas SD de 2 TB, así que probablemente compre una cuando baje el precio.
    • En Android siempre tuve problemas inestables con la imagen de portada y el título.
      A veces los cambios no se aplicaban, o se aplicaban y luego de repente cambiaban a una portada equivocada; por lo que veía, parecía un bug de Android.
      Me da curiosidad si te pasó algo así.
  • Creé mi propia app web para poder escuchar álbumes completos, pero también pausar en medio o cambiar de dispositivo.
    Me gusta escuchar los álbumes de principio a fin, pero al menos YouTube Music no recuerda la posición de reproducción, y era incómodo tener que abrir de nuevo el álbum en otro dispositivo y buscar el punto donde me quedé.
    En mi app web, pegas una URL, el servidor la descarga con yt-dlp y puedes streamear desde ahí; siempre recuerda la posición de reproducción, así que puedo escuchar en el teléfono en el auto y luego continuar en la laptop en el trabajo.
    También encaja bien para agregar mixes de otras fuentes, como NTS Radio.

    • Justo lo que más me frustraba de YouTube Music era una cola que se pudiera guardar y una transición más fluida entre dispositivos.
      Si está publicada, me gustaría echarle un vistazo a esa app web.
  • Todavía no lo leí completo, pero es un buen artículo, y me gusta leer cómo y por qué un desarrollador tomó decisiones más detalladas.
    Coincido con la sensación de que los reproductores de audio que probé no me convencen.
    En el mundo de las apps de música, la disposición de la pantalla y la UI parecen haberse vuelto casi universales, pero a mí no me gustan y tampoco las entiendo del todo.
    Se siente como pelearme con todas las apps de música, así que da gusto ver a alguien intentando crear algo nuevo.

  • Todavía uso la app Apple Music con mis archivos locales.
    Tengo desactivado el servicio de streaming de Apple Music, importé todo a la app Apple Music en macOS y luego conecto el teléfono a la laptop, como en 2007, para sincronizarlo como si fuera un iPod.
    Todo funciona como esperaba, y como mi música no cambia con frecuencia, la sincronización no es un problema.
    Sincronizar por cable también me da una extraña nostalgia.

    • Hasta donde sé, la sincronización automática por Wi‑Fi con iTunes también sigue funcionando bien.
  • Me cuesta estar de acuerdo con que, antes de que existiera async/await, el código de concurrencia en Swift se sintiera más tosco y con más boilerplate que en Go o JS/TS.
    El procesamiento asíncrono puede facilitar escribir código concurrente, pero a medida que la base de código crece, puede volverse menos sencillo razonar sobre ella.
    En bases de código asíncronas complejas, es más difícil entender el flujo del código y la concurrencia.
    Si el objetivo es reducir el costo de ejecución del código en threads, existe la solución de los green threads ligeros; si el objetivo es reducir el costo de mantenimiento del código con threads, es probable que lo asíncrono exija más esfuerzo a largo plazo.

    • Una buena concurrencia debería hacer que entender y razonar sobre el código sea más simple a medida que crece.
      Incluso solo con encapsulación basada en procesos o servicios se obtienen grandes ventajas.
      Creo que esto no es tanto un fracaso de la concurrencia en sí, sino más bien una limitación de la abstracción async/await.
    • En este caso, el objetivo es un reproductor de audio simple, así que probablemente no sea un gran problema.
  • Esperaba que el artículo hablara no solo de software para gestionar y reproducir canciones, sino también de dispositivos físicos.
    Hace unos años quise comprarle un reproductor MP3 a mi hijo de 10 años, que de verdad disfrutaba escuchar música pero todavía no estaba listo para usar un teléfono, y me sorprendió lo terribles que eran las opciones.
    Creo que Apple dejó un gran vacío que nadie logró llenar cuando descontinuó el iPod.
    El iPod shuffle en forma de memoria USB fue el mejor reproductor MP3 que he usado: era pequeño, se podía conectar sin cable aparte y la batería duraba mucho.
    No tenía pantalla para explorar música, pero esa era la idea; bastaba con dejarlo en shuffle.
    Es una lástima que ni siquiera un concepto relativamente simple como ese se haya replicado bien en el mercado de hardware.
    La gente dirá que no es un problema de hardware sino de software/DRM, pero de verdad es una pena que no haya buenos dispositivos portátiles baratos para reproducir música.

    • Creo que el verdadero cambio aquí no fue la desaparición del iPod.
      La existencia de Spotify y los smartphones mató a los reproductores MP3, y entre ambos ocuparon tanto aire del mercado que asfixiaron al resto.
    • Fiio fabrica bastantes productos en esta categoría.
      Por ejemplo, están https://www.fiio.com/cp13 y https://www.fiio.com/jm21.
    • Hay muchos iPod usados, y varios de los modelos antiguos con disco duro son bastante fáciles de modificar con almacenamiento flash, así que pueden ser una opción.
      También sirve como un pequeño proyecto educativo para aprender electrónica y reutilizar dispositivos “viejos”.
      Hay muchas opciones para personalizar la carcasa, y también se pueden hacer modificaciones más profundas, como agregar Bluetooth o USB‑C.
      Como dijeron otros, el lado de software también sigue funcionando bien con iTunes en Windows y la integración Finder/Music en macOS.
    • Creo que es más un problema de demanda que de hardware o software.
      Los fabricantes chinos venden por 50 a 100 dólares dispositivos que se ven bien, reproducen música y también tienen funciones de smartphone, como Mini iPhone 16 o Mini S24.
      Es más probable que los padres les compren a sus hijos dispositivos de ese estilo en lugar de reproductores MP3.
      No darle un teléfono a un hijo de 14 años es una forma de crianza poco convencional, y la respeto, pero no parece que esa demanda sea mayor que la oferta que hoy puede existir.
    • Sony todavía fabrica reproductores bastante buenos bajo la marca Walkman: https://electronics.sony.com/audio/walkman-digital-recorders...
      Para un niño de 10 años pueden ser caros, pero quizá se pueda encontrar alguno usado en eBay.
  • La combinación de gestionar música local con Music.app y usar la sincronización de iPhone desde Finder todavía me funciona bien.
    Dicho eso, Music.app parece llevar años sin mantenimiento desde la terrible reescritura de Big Sur, y tiene bugs molestos.
    A pesar de sus defectos, esta combinación es la mejor solución de gestión de biblioteca musical + sincronización móvil que he visto, pero como el software no se mantiene y tampoco es multiplataforma, siento que al final tendré que escribir mi propio sistema.

    • Parte de los problemas de la “reescritura” de Music está en que no parece tanto una reescritura real como algo copiado y pegado desde iTunes.
      Siguen quedando muchas rarezas propias de iTunes, como la ventana modal de ajustes/preferencias, una reliquia de la época de OS 9.
      Una ventana de ajustes de OS X no debería ser modal.
      Me da la impresión de que internamente deben estar trabajando en una versión hecha desde cero, como Music para Windows basado en WinUI, que fue creado completamente de nuevo.
    • En mi iPod Video de 5.ª generación, la sincronización integrada de Finder todavía está bastante rota.
      Lo más molesto era la sincronización de podcasts: en la época en que iTunes se encargaba de eso funcionaba bien, pero en la sincronización de Finder de Big Sur hay un bug por el que, si vas a otra pista o podcast y luego vuelves, no recuerda la posición de reproducción.
      Eso hace que los podcasts de más de unos minutos sean prácticamente difíciles de usar en el dispositivo.
    • También puedes probar mi app: https://www.plastaq.com/minimoon
  • La gente, en lugar de cambiarse a Android, de verdad termina creando su propia app de música.
    Me pregunto si será por las burbujas azules o porque todo “simplemente funciona”, salvo la reproducción de música sin conexión.

    • ¿De verdad te mudarías a otro país solo porque el pan sabe mejor?
      No veo cuál es el problema de hornearlo uno mismo.
    • Hice un prototipo de reproductor de música para iOS.
      Fue porque la app de VLC no podía parsear de forma confiable los metadatos de los FLAC en mi servidor de archivos, y por límites de almacenamiento tampoco podía guardar toda mi colección de música en el dispositivo.
      Mi app sigue siendo un prototipo en el sentido de que quiero más funciones, pero ya tiene reproducción de audio, acceso a archivos remotos y parseo de metadatos FLAC, así que funciona lo suficiente como para que me falte motivación para implementar funciones extra.
      Igual que el autor, al principio quería usar React Native porque tenía experiencia y algunas apps en mantenimiento, pero no quería apuntar a otras plataformas ni depurarlas.
      Así que decidí probar SwiftUI y, con una herramienta especial[1], armé algo parecido a hot reload.
      Es un hack bastante sucio que requiere meter flags personalizados de linker en Xcode, pero funciona lo suficiente como para no extrañar la experiencia de desarrollo de TypeScript y React Native.
      [1] https://github.com/johnno1962/InjectionIII
    • La gente de verdad escribe código con tal de evitar Android.
    • foobar2000 lleva mucho tiempo en la App Store.
      La verdad, me parece un poco raro que el autor no haya encontrado una app gratuita de música que le gustara.
      Aun así, no hace falta tener una excusa para crear algo, así que quizá solo era una narrativa para empezar la historia.
    • En iOS estoy usando la combinación de apps navidrome/substreamer y funciona bien.
      También permite guardar en el dispositivo la música que elijas.
  • Si tienes una colección de música sin conexión, realmente recomiendo https://play.google.com/store/apps/details?id=in.krosbits.mu...
    Funciona muy bien, sin problemas

    • Symfonium también es excelente
      También admite Plex, Jellyfin, WebDAV, SMB, etc.