Spotufi: Gerçek Spotify Hesabınız İçin Yerel Android İstemcisi, Spotify'ın Kendi Yayınını Çalar
🇹🇷 Spotufi'ye teknik bakış: Spotify Web Player'ı görünmez bir Chromium WebView içinde çalıştıran; çevrimdışı FLAC indirme, senkronize şarkı sözü, transkript, scrobble ve 13 dil desteği sunan açık kaynaklı Android müzik istemcisi.
Resmî olmayan Spotify istemcilerinin çoğu çalma işini aynı yolla çözer: şarkıyı Spotify’da arar, sonra başka bir kaynaktan “eşleşen” bir parçayı çalar. Ortaya Spotify görünümlü bir arayüz, ama altında farklı bir ses kaynağı çıkar; yanlış versiyonlar, canlı kayıtlar ve bulunamayan parçalar da bu yaklaşımın bilinen sorunlarıdır.
Spotufi tam tersi bir yol izler. Gerçek bir Spotify hesabıyla giriş yapar, resmî Spotify Web Player’ı görünmez bir Chromium WebView içinde çalıştırır ve Spotify’ın sunduğu, cihazda Widevine ile çözülen yayını çalar. Motorun etrafındaki her şey (kütüphane, arama, kuyruk, şarkı sözleri, indirmeler, scrobble, Android Auto) yerel Kotlin ve Jetpack Compose ile yazılmıştır. | 🇬🇧 English
📦 İndir: GitLab Releases | Obtainium | 💻 Repo: gitlab.com/fr0stb1rd/spotufi
🎧 Spotufi Nedir?
Spotufi, varsayılan ve normal kaynağı Spotify’ın kendisi olan çok kaynaklı bir Android müzik çalardır. Free ve Premium hesapları destekler; çalma listelerini, albümleri, sanatçıları, radyoları, podcast’leri ve sesli kitapları çalar. Bunların üzerine çevrimdışı kayıpsız indirme, crossfade, scrobble, kelime senkronlu transkript ve yerel dinleme özeti gibi özellikler ekler.
| Özellik | Değer |
|---|---|
| Paket | io.github.sekademi.spotufi |
| Platform | Android 10 (API 29) ve üzeri |
| Dil / Arayüz | Kotlin 2.4, Jetpack Compose, Material 3 |
| Mimari | MVVM, manuel DI, 5 Gradle modülü |
| Çalma motoru | Headless Chromium WebView içinde Spotify Web Player (Widevine) |
| Sistem entegrasyonu | Media3 MediaLibraryService (bildirim, kilit ekranı, kulaklık, Android Auto) |
| Diller | 13 |
| Lisans | GPL-3.0-or-later |
🔊 Gerçekte Ne Çalıyor?
Bu tür uygulamaların şarkıyı Spotify’da bulup bir YouTube eşleşmesi çaldığı sanılır. Spotufi’de dört ayrı ses yolu vardır ve yalnızca biri varsayılandır:
| Yol | Ne zaman kullanılır | Ses nereden gelir |
|---|---|---|
| Spotify Web Player (varsayılan) | Her normal çalma: çalma listeleri, albümler, sanatçılar, radyolar, podcast’ler, sesli kitaplar | Spotify’ın kendi CDN’i; cihazın Widevine modülü çözer. Resmî uygulamayla aynı kayıt. |
| BitChord / NewPipe yedeği | Yalnızca Spotify reddettiğinde: hesapta 402 lisans/ülke kısıtı ya da cihazda kullanılabilir Widevine olmaması |
Başka bir kaynaktan eşleşen parça; çalma durmasın diye. Kurtarma yoludur, motor değildir. |
| Kayıpsız FLAC | Yalnızca kullanıcının istediği indirmelerde | SpotiFLAC aynı kaydı TIDAL / Qobuz / Amazon’dan bulur ve ISRC ile (yoksa başlık + sanatçı, 3 sn içinde süre) doğrular. |
| Yerel dosyalar | Cihazdaki ses dosyaları | Cihaz depolaması; Media3 ExoPlayer ile çalınır. |
flowchart TD
A["Çalma isteği"] --> B{"Kaynak?"}
B -->|"Yerel dosya veya indirme"| C["ExoPlayer (LocalPlayer)"]
B -->|"Spotify öğesi"| D{"Widevine kullanılabilir ve 402 reddi yok mu?"}
D -->|"Evet"| E["Headless WebView içinde Web Player"]
D -->|"Hayır"| F["BitChord + NewPipeExtractor yedeği"]
E --> G["Media3 oturumu: bildirim, kilit ekranı, Android Auto"]
F --> G
C --> G
Yayın kalitesi Web Player’ın kendi seviyelerini izler: Auto, Low, Normal, High. Yapay bir “320 kbps” anahtarı yoktur; motor Spotify’ın verdiğini çalar. Kayıpsız ses indirme yoluna aittir, yayına değil.
🏗️ Mimari
Spotufi, Jetpack Compose üzerinde MVVM’dir ve arayüzün hiçbir zaman çalma host’una bağımlı olmaması için katmanlara ayrılmıştır. Tüm Android modülleri base/ altındadır.
| Modül | Tür | Amaç |
|---|---|---|
:app |
Android uygulaması (compileSdk 37, minSdk 29) | Arayüz, navigasyon, ViewModel’ler, DI, indirmeler, sözler, scrobble |
:api |
Saf JVM kütüphanesi (Java 21) | Arayüz ile motor arasındaki arayüzler ve veri modelleri. Android yok, Spotify ayrıntısı yok |
:api-spotify |
JVM kütüphanesi (Java 21) | SpotifyApi: :api‘nin implementasyonu, GraphQL istemcisi ve bridge protokolü |
:host-android |
Android kütüphanesi | WebView host’u (WebPlayerHost), PlaybackService, ağ yığını |
:host-cdp |
Saf JVM kütüphanesi | Otomatik canlı testler için DevTools Protocol üzerinden masaüstü Chrome host’u |
flowchart TB
subgraph APP[":app"]
UI["Compose ekranları + ViewModel'ler"]
SP["SongPlayer facade"]
UI --> SP
end
API[":api (sözleşme)"]
SPOT[":api-spotify (SpotifyApi + Bridge)"]
BH["BridgeHost arayüzü"]
HA[":host-android (WebView + PlaybackService)"]
HC[":host-cdp (masaüstü Chrome, testler)"]
WP["open.spotify.com Web Player + player-bridge.js"]
SP --> API
SPOT -. uygular .-> API
SPOT --> BH
HA -. uygular .-> BH
HC -. uygular .-> BH
HA --> WP
HC --> WP
:app modülü çalma ile yalnızca :api üzerinden konuşur. :host-android ve :host-cdp aynı host sözleşmesinin iki implementasyonudur; bu yüzden aynı bridge betiği ve üstündeki API katmanı, hem gerçek bir telefonda hem de masaüstü Chrome’da canlı testlerle çalıştırılabilir.
Bağımlılık bağlama manuel yapılır: tek bir Graph nesnesi, annotation-processing tabanlı bir framework yok.
Player Bridge
Çekirdekteki fikir, resmî Web Player sayfasına enjekte edilen küçük bir betiktir: player-bridge.js. Sayfayı yamalamaz. React’in context ağacını gezerek sayfanın kendi servis kayıt defterini (service registry) bulur ve sayfanın zaten kullandığı servisleri çağırır (PlayerAPI, PlaybackAPI, LibraryAPI, TokenProvider).
| Yön | Android | Masaüstü (CDP) |
|---|---|---|
| Host → sayfa | evaluateJavascript("Myspoti.call(id, method, args)") |
Runtime.evaluate, aynı çağrı |
| Sayfa → host | MyspotiHost.postMessage(json) (web message listener, yalnızca open.spotify.com) |
Runtime.addBinding("__mspHostPost") |
Sayfadan gelen her mesaj tek bir JSON dizesidir: ready, state, queue, result (çağrı id‘si ile) ve error. Oynatıcı durumu; parçayı, context URI’yi, duraklatma/tamponlama bayraklarını, positionMs ile örneklendiği timestamp‘i (host, çalarken konumu ekstrapole eder), süreyi, shuffle’ı, repeat modunu (kapalı / context / parça), hızı, restrictions alanını ve playbackId‘yi taşır.
Pratikte önemli olan bazı tasarım ayrıntıları:
- Mesaj kanalı üzerinden istek/yanıt. Her komut artan bir id ve bir
CompletableDeferredalır. Android’inevaluateJavascript‘i Promise’leri beklemediği için sonuçlar her zamanresultmesajı olarak gelir. - Tasarımdan gelen backpressure yönetimi. Olay akışları
DROP_OLDESTkullanır. Ana thread takılırken önemli olan tek şey en yeni durumdur; varsayılanSUSPENDpolitikası tam da o değeri düşürürdü. - Beklenmedik girdiye dayanıklılık. Sayfa mesajlarını işleyen collector ana thread’de çalışır ve orada yakalanmamış bir exception uygulamayı kapatır. Bu yüzden her mesaj
runCatchingiçinde işlenir; bozuk olanlar loglanıp atılır. - Kısıtlamalara uyulur. Komutlar sayfanın
restrictionsalanı tarafından reddedilebilir (örneğin sıradaki parça yüklenirken) ve hata mesajı bunu söyler.
Dokunuştan Sese
sequenceDiagram
participant U as Kullanıcı
participant UI as Compose UI
participant API as MyspotiApi.player
participant B as Bridge (Kotlin)
participant JS as player-bridge.js
participant WP as Web Player servisleri
participant S as Spotify CDN ve lisans sunucusu
U->>UI: Parçaya dokunur
UI->>API: play(contextUri, trackUri)
API->>B: call(id, player.play, args)
B->>JS: evaluateJavascript Myspoti.call
JS->>WP: PlayerAPI / PlaybackAPI
WP->>S: Ses isteği + Widevine lisansı
S-->>WP: Şifreli yayın
WP-->>JS: Durum değişimi
JS-->>B: postMessage (state JSON)
B-->>API: StateFlow güncellemesi
API-->>UI: Recomposition
Oturumun sahibi Spotify’dır. Sonraki, önceki, shuffle, repeat ve kuyruk Spotify’ın kendisine aittir; böylece çalma hesapla ve diğer cihazlarla senkron kalır. Başka bir Connect cihazı çalmayı devralırsa Spotufi onu geri alır. Bir Media3 PlaybackService / MediaLibraryService, bu oturumu bildirime, kilit ekranına, kulaklık tuşlarına ve Android Auto’ya yansıtır.
Okuma ve Yazma Ayrımı
:api-spotify katmanı trafiği amacına göre ikiye ayırır:
- Okumalar (arama, katalog, ana sayfa, kütüphane listeleri, sözler, kullanıcı verisi) sayfanın web token’ı ve uygulamanın ürettiği bir client token ile doğrudan Spotify’a HTTP üzerinden gider. GraphQL yüzeyi geniştir:
SpotifyApi.raw, güncel Web Player build’indeki 179 GraphQL operasyonunun tamamına ve henüz tiplenmemiş her şey için 89 sayfa servisine ulaşabilir. Operasyon hash’lerigql-ops.jsoniçinde durur ve genel bundle’ları okuyan bir araçla yenilenir. - Yazmalar ve çalma (play, kuyruk düzenleme, kütüphaneye kaydetme, klasörler, Connect, Jam) bridge üzerinden sayfanın kendi servislerine gider.
Repodaki docs/api/ klasörü, yakalanan oturum akışını, GraphQL değişkenlerini ve kapsam durumunu belgeler (son raporda 179 operasyonun 44’ü belgelenmiş, 58’inin değişkenleri biliniyor).
✨ Özellikler
▶️ Çalma
- Gerçek Spotify hesabı, gerçek Spotify yayını (Free veya Premium)
- Çalma listeleri, albümler, sanatçılar, radyolar, podcast’ler, show’lar, sesli kitaplar
- Spotify Connect farkındalığı: başka cihazın devraldığı çalma geri alınır
- Sürükle-bırak sıralamalı kuyruk, sıradaki olarak çal, araya ekle; Spotify’ın kendi shuffle ve repeat’i
- Oturum geri yükleme: kuyruk, parça ve konum yeniden başlatmadan sonra korunur
- Uyku zamanlayıcısı, gerçek Spotify önerileriyle otomatik oynatma/radyo
- 0 ile 12 sn arası crossfade (örtüşme değil, geçiş yumuşatma)
- Albüm kapağına göre dinamik renkli mini oynatıcı ve sürüklenebilir arama çubuğu
- Aşağı kaydırarak kapatma hareketi (müzik ve podcast oynatıcısında)
- Jam: oturum başlat ve davet bağlantısını paylaş, ya da QR tarayarak veya bağlantıyı yapıştırarak katıl
🔍 Keşfet ve Kütüphane
- Filtreli (Tümü / Müzik / Podcast’ler / Sesli Kitaplar) kişiselleştirilmiş ana sayfa akışı
- Parça, albüm, sanatçı, çalma listesi ve podcast araması; küratörlü çalma listeleriyle tür kategorileri
- Sanatçı sayfaları: biyografi, doğrulanmış rozeti, diskografi, benzer sanatçılar
- Zevk profili: bir parçayı veya sanatçıyı radyo ve mix’lerden hariç tut
- Pil tasarrufu anahtarlı Canvas video döngüleri
- Beğenilen şarkılar, kayıtlı albümler, takip edilen sanatçılar ve çalma listeleri için tek kütüphane; liste veya ızgara görünümü ve özel sıralama
📝 Şarkı Sözleri
Söz çözümleyici kaynakları sabit bir sırayla dener:
flowchart LR
A["Parça"] --> B["Spotify color-lyrics (track id ile)"]
B -->|"yok"| C["LRCLIB (satır senkronlu veya düz)"]
C -->|"yok ve opt-in açık"| D["NetEase / Genius"]
B -->|"var"| R["Tam ekran görünüm"]
C -->|"var"| R
D -->|"var"| R
- Spotify’ın kendi color-lyrics’i önce gelir: resmî uygulamadakiyle aynı metin, renkler ve künye.
- LRCLIB açık yedektir (satır senkronlu veya düz). Yedek sonuç yalnızca başlık, sanatçı ve süre parçayla eşleşirse saklanır.
- NetEase ve Genius opt-in’dir, varsayılan olarak kapalıdır.
- Otomatik kaydırmalı tam ekran görünüm, herhangi bir satıra dokunarak sarma, isteğe bağlı satır çevirisi, arka planda ön yükleme ve kalıcı söz önbelleği.
🎙️ Podcast ve Sesli Kitaplar
- Show sayfalarında, bölüm sayfasında ve ana sayfada, Spotify web oynatıcısındaki gibi bölüm ilerleme çubukları
- Podcast oynatıcısında oynatma hızı (0,5x ile 3,5x), atlama düğmeleri ve uyku zamanlayıcısı
- Kelime düzeyinde önizleme ve tam okuyucuyla transkriptler: konuşmacı etiketleri, çalmayı izleyen vurgulama, otomatik takip ve kopyalama
📻 Scrobble ve Geçmiş
Üç servis desteklenir ve her birinin kendi anahtarları ile eşikleri vardır: Last.fm, ListenBrainz ve Libre.fm. Şimdi çalıyor güncellemeleri ve scrobble’lar toplu gönderilir; ListenBrainz’de bir parça atlanınca now-playing kaydı silinir; kabul edilen/yok sayılan yanıtlar API’den okunur ve oturum bozuksa ipucu gösterilir.
Sayılma kuralları servise göre değişir:
| Servis | Eşik |
|---|---|
| Last.fm | min(süre × yüzde, maksSaniye); yüzde 10–100, maksimum 30–180 sn aralığına sıkıştırılır |
| ListenBrainz | min(süre / 2, 4 dk) |
Eşiği geçen dinlemeler önce kalıcı bir outbox‘a yazılır; böylece ağ kesintilerinden ve işlemin yeniden başlamasından etkilenmez:
flowchart LR
A["Oynatma durumu"] --> B{"Eşik aşıldı mı?"}
B -->|"Evet"| C["Diskte ScrobbleOutbox"]
C --> D["Servis başına toplu gönderim"]
D -->|"Kabul edildi"| E["Outbox'tan sil"]
D -->|"Ağ hatası"| F["attempts + 1, nextAttemptAt backoff"]
F --> C
Her kaydın deterministik bir id’si vardır (servis | hesap | parça | başlangıç), dolayısıyla aynı dinlemeyi tekrar kuyruğa almak etkisizdir.
Bununla birlikte, uygulamanın kendi dinleme geçmişinden Recents ekranı ve yerel bir Recap (30 gün / 12 ay / tüm zamanlar) üretilir; süreler kaynağa göre (Spotify, indirmeler, yerel dosyalar) ayrılır.
🎨 Arayüz ve Platform
- Vurgu rengi ve saf siyah AMOLED temasıyla Material 3
- Opak veya yarı saydam alt çubuklar; liste veya ızgara kütüphane
- Dikey, yatay veya otomatik döndürme; kapağın altında sözler
MediaLibraryServiceüzerinden Android Auto- GitLab release’lerine karşı yerleşik güncelleyici
- Geliştirici araçları: motor durumu, log kopyalama, oynatıcıyı yeniden başlatma, kurtarma testi
- 13 dil: İngilizce, Türkçe, Azerbaycanca, Almanca, İspanyolca, Fransızca, İtalyanca, Portekizce, Rusça, Endonezce, Japonca, Çince (Basitleştirilmiş), Hintçe
💾 Çevrimdışı: İndirmeler, Önbellek ve Veri Tasarrufu
Kayıpsız İndirmeler
İndirmeler hiçbir şeyi yeniden kodlamaz. İstek, kayıpsız bir katalogdaki aynı kayda çözülür ve dosya sunulduğu haliyle saklanır.
| Ayar | Seçenekler |
|---|---|
| İndirme kalitesi | Kapalı, küçük AAC, FLAC 16-bit, FLAC 24-bit veya her seferinde sor |
| Eşleşme kuralı | Önce ISRC; yoksa başlık + sanatçı ve 3 sn içinde süre |
| Kaynaklar | SpotiFLAC; TIDAL (DASH), Qobuz ve Amazon üzerinden çözer |
flowchart LR
A["Spotify metadata + ISRC"] --> B["SpotiFLAC çözümleyici"]
B --> C{"ISRC eşleşti mi?"}
C -->|"Evet"| D["Aday yayın"]
C -->|"Hayır"| E{"Başlık + sanatçı eşleşti, süre 3 sn içinde mi?"}
E -->|"Evet"| D
E -->|"Hayır"| X["Kullanılamıyor"]
D --> F{"DASH manifest mi?"}
F -->|"Evet"| G["DashFlacRemux ile tek .flac"]
F -->|"Hayır"| H["Tek dosya FLAC"]
G --> I["Şifreli çevrimdışı depo"]
H --> I
I --> J["Doğrulanır, sonra hazır işaretlenir"]
ffmpeg olmadan DASH. Hi-res FLAC, parçalı bir DASH manifest olarak gelir. Segmentler ham FLAC frame’leri taşıyan fragmented MP4’tür; bu yüzden uygulama gerçek bir .flac dosyasını doğrudan üretir:
- STREAMINFO, init segmentinin
dfLakutusundan alınır. - Her medya segmentinin
mdatyükü (FLAC frame’leri) art arda eklenir; birkaç segment paralel indirilir. fLaC, STREAMINFO metadata bloğu ve ardından frame’ler yazılır.
Şifreli Depolama
İndirilen dosyalar diskte düz ses değildir. Dosya başına rastgele anahtarla AES-CTR kullanılır; anahtar, cihazdan (ve güvenli donanımından) hiç çıkmayan bir Android Keystore anahtarıyla sarılarak saklanır. Dosya düzeni MSE1, 16 baytlık IV ve ardından şifreli veridir. CTR modu rastgele erişimi ucuz tutar; şifreli dosyada sarma, baştan çözmeden çalışır. Uygulamanın dışına kopyalanan bir dosya gürültüden ibarettir.
Oynatma Önbelleği ve Ön Yükleme
- Depolama sınırlı oynatma önbelleği: 500 MB ile 5 GB arası ya da sınırsız
PlaybackCacheManager, seri çalışan ve en son kuyruk kazanır mantıklı bir ön yükleyicidir: istekler conflated bir kanaldan geçer; hızla değişen kuyruk önbellek I/O’sunu yığmaz ve kuyruk değişiklikleri çağıran thread’de disk işi yapmazQueuePreloader, aktif kuyruğu izler ve mevcut ile sıradaki parçaları önceden önbelleğe alır
Veri Tasarrufu
Mobil veride veya ölçümlü (metered) herhangi bir ağda (Wi-Fi’de asla), Veri Tasarrufu anahtarı uygulamayı çevrimdışıymış gibi davranmaya zorlar: her bağlantı reddedilir (ağ kontrolleri, OkHttp interceptor’ı, WebView ve Glide korumaları), gizli Spotify oynatıcısı kapatılır ve yalnızca indirmeler ile yerel dosyalar çalar. Wi-Fi’ye dönünce oynatıcı yeniden kurulur.
🧩 Mühendislik Notları
Yerel dosyalar devri temiz alır. LocalPlayer (ExoPlayer) aktifken Spotify motorunu duraklatır; SongPlayer kontrolleri (oynatıcı, mini oynatıcı, bildirim) o an hangi motor aktifse ona yönlendirir. Spotify’dan yeniden bir şey çalınca kontrol motora geri verilir.
SongPlayer bir facade’dır. Ekranlar playSong, pause, seekTo gibi metotları çağırır; her komut API üzerinden Web Player’a gider ve durum akışlardan geri gelir. Sonraki/önceki/shuffle/repeat Spotify’ın kendisinde kalır.
Rate limit farkındalığı. Host, ses lisansı 429 yanıtlarını kayan bir pencerede izler. Bir yığılmadan sonra yeni denemeleri giderek uzayan bir süre boyunca durdurur ve licenseLimitedUntil değerini uygulamaya açar; durumu daha da kötüleştiren bir yeniden deneme fırtınasını beslemek yerine geri çekilir.
Reddedilen komutların teşhisi. Bazı free hesaplarda Connect komutları 402 COMMAND_FAILED alır. Özel bir tanılama özeti yanıtları rota başına gruplar (gecikme, yanıt başlıkları, hata türü, başka bir rotadaki başarının sesi 10 sn içinde gerçekten başlatıp başlatmadığı); böylece bir rapor, eskiden loglarda kazı yapmayı gerektiren soruyu tek ekranda çözer. İçinde id, token veya adres bulunmaz.
Compose performans çalışması ölçüme dayanır. Repoda 231 composable ve 3.494 composable argümanı üzerinde bir Compose compiler denetimi ile odaklı düzeltmeler bulunur: liste anahtarları, lazy satırlarda content type, scroll aşaması okumalarının composition dışına alınması, memoize edilmiş kütüphane birleştirme, lifecycle farkındalıklı collect. Somut bir örnek: hızlı kaydırma tutamacı firstVisibleItemIndex‘i composition’da okuyor ve geçilen her öğede yeniden compose oluyordu; okumayı offset { } lambdasına taşımak scroll frame’lerini layout aşamasına hapseder. Raporlar sınırları açıkça belirtir: ölçüm olmadan çalışma zamanı kazancı iddia edilmez. Boyut raporuna göre evrensel release APK yaklaşık 16,9 MB’dir (arm64-v8a yaklaşık 14,6 MB).
🛡️ Gizlilik ve Çökme Raporları
Spotufi Google Play dışında dağıtılır; bu yüzden belirli bir telefonda çöktüğünde kimse haberdar olmaz. Bunu çözmek için release build’leri measure.sh (açık kaynak, Apache-2.0) üzerinden çökme raporu gönderebilir.
- Varsayılan olarak açık, girişten sonraki kurulum ekranında bir kez açıklanır; Ayarlar → Çökme raporu gönder kapatır ve SDK’yı tamamen durdurur
- Gönderilenler: çökmeler (Kotlin/Java ve native), ANR’ler, gerçek arıza gösteren atlatılmış hatalar (Web Player renderer’ının ölmesi, Widevine eksikliği), cihaz modeli, Android ve uygulama sürümü, yerel ayar, son ekranlar ve dokunuşlar
- Çökme ekran görüntüleri, Measure panosunda seçilen seviyeye göre telefonda maskelenir (önerilen: metin ve girdileri gizle, tıklanabilir kontrolleri göster)
- Asla gönderilmeyenler: Spotify hesabı, parola, token’lar, kütüphane, çalma listeleri, dinleme geçmişi; ağ URL’leri toplanmaz
- Debug build’leri ve kimlik bilgisi olmayan fork’lar hiçbir raporlama içermez
🚀 Kurulum
| Kaynak | Not |
|---|---|
| Obtainium | GitLab release’lerini izler ve otomatik günceller |
| GitLab Releases | Elle indirme |
Release APK’ları ABI başına bölünür (arm64-v8a, armeabi-v7a, x86_64, x86); yanında bir evrensel build ve SHA-256 sağlama toplamları bulunur. Android 10 (API 29) veya üzeri gerekir. Kurulumdan sonra uygulama kendini güncelleyebilir: güncelleme denetleyicisi GitLab release’lerini en fazla üç saatte bir sorgular, ilgili CHANGELOG.md bölümünden alınan sürüm notlarını gösterir, ardından imzalı APK’yı indirip kurar.
Release Hattı
flowchart LR
T["vX.Y.Z etiketi"] --> K["Keystore'u çöz"]
K --> V["Kimlik bilgilerini doğrula"]
V --> B["assembleRelease: R8 + ABI split"]
B --> U["APK'lar + SHA-256 Generic Package Registry'ye"]
U --> R["CHANGELOG bölümüyle GitLab Release"]
R --> A["Uygulama içi UpdateChecker"]
R --> O["Obtainium"]
Kaynaktan Derleme
Gereksinimler: JDK 21 LTS ve Android SDK 37.
1
2
3
4
5
6
7
8
9
git clone https://gitlab.com/fr0stb1rd/spotufi.git
cd spotufi/base
./gradlew assembleDebug --warning-mode all
# release build
./gradlew :app:assembleRelease --no-daemon --warning-mode all
# birim testleri
./gradlew testDebugUnitTest :api:test :api-spotify:test :host-cdp:test --warning-mode all
Debug APK’ları base/app/build/outputs/apk/debug/ altına düşer. Tüm bağımlılık ve toolchain sürümleri base/gradle/libs.versions.toml ile Gradle wrapper’da tutulur. Proje kuralları ayrıca @Suppress / @SuppressLint kullanımını yasaklar (uyarılar susturulmaz, düzeltilir) ve semantik sürümlemeyi zorunlu kılar.
⚖️ Sınırlar ve Ödünleşimler
Motor, resmî bir web sayfasını onun iç servisleri üzerinden sürer. Yayını özgün kılan şey budur, ama aynı zamanda projeyi Web Player’ın yapısına bağlar. Spotify tarafındaki bir değişiklik, güncellenene kadar bridge’i veya GraphQL operasyon hash’lerini bozabilir.
- Yayın kalitesi Web Player’ınkidir; keyfi bir bitrate ayarı yoktur
- Jam: Spotify, free hesaptan Jam başlatmaya
RESTRICTEDyanıtı verir; katılma ve geri kalanı bağlıdır - Özel oturum Web Player tarafından desteklenmez ve arkadaş etkinliği boş liste döner
- İzin verilmeyen veya bölge kilitli parçalar yedek motoru devreye alır; bu, tanımı gereği farklı bir kaynaktır
- Spotufi, Spotify AB ile bağlantılı değildir ve onaylanmamıştır. Proje eğitim amaçlıdır; kullanıcılar sanatçıları Spotify Premium ve resmî satın alımlarla desteklemeye teşvik edilir
🙏 Teşekkür ve Lisans
Spotufi, birçok açık kaynak projenin üzerine kuruludur: Spotifusion, SpotiDuck, Myspoti / Spotui (Web Player WebView bridge mimarisi), BitChord ve NewPipeExtractor (yedek yol), SpotiFLAC (kayıpsız çözümleme), LRCLIB (senkronize sözler), Measure (çökme raporlama), SimpMusic (crossfade / ses filtreleri), Metrolist, Meld, Neptune ve Spotui fork’ları. Tüm bildirimler THIRD_PARTY_NOTICES.md dosyasındadır.
Spotufi, GNU GPL v3 veya üzeri altında özgür yazılımdır.
Kaynak kod, issue’lar ve sürümler: gitlab.com/fr0stb1rd/spotufi















