ספרי שמע הם תוכן מאתגר: קבצים של 15 שעות, חיבורים לא יציבים, ומשתמשים שדורשים חידוש מדויק של המיקום. באג טיפוסי הוא נגן שמאבד את ההתקדמות במעבר בין Wi-Fi ל-LTE, ושעון שינה שחותך פרק ללא דעיכה. פתרנו זאת ברמת הארכיטקטורה: שמירה במטמון אגרסיבית באמצעות משימות רקע של URLSession, סנכרון מיקום עם אחסון בענן כל 5 שניות, ודעיכת עוצמה חלקה לפני עצירה. התוצאה: האפליקציה לא קורסת, המיקומים נשמרים, והמשתמשים לא מתלוננים.
פורמטי קבצים והזרמת השמעה
ספרי שמע מגיעים בעיקר ב-MP3 (הנפוץ ביותר), M4B (AAC עם סמני פרקים, תקן אפל), M4A ו-OGG/Opus. M4B מועדף לקטלוגים חדשים—הוא מכיל באופן טבעי פרקים, תמונת עטיפה ומטא-דאטה ללא קובץ צד.
הזרמת השמעה (ללא הורדה מלאה לפני השמעה): ב-iOS, AVPlayer פותח כתובת HTTP ומתחיל לנגן ללא טעינה מלאה. באנדרואיד, ExoPlayer (Media3) עושה את אותו הדבר באמצעות DefaultDataSource.Factory. עבור קבצים ארוכים (15 שעות ≈ 800 MB), זה קריטי.
פרקים מ-M4B: AVAsset.loadChapterMetadataGroups(bestMatchingPreferredLanguages:) מחזיר AVTimedMetadataGroup[] עם חותמות זמן וכותרים. באנדרואיד, השתמש ב-MediaMetadataRetriever.extractMetadata וניתוח מבנה תיבות MP4 עבור אטומי פרקים, או השתמש ב-ExoPlayer עם MetadataRetriever מותאם אישית.
למה M4B הכי טוב לקטלוגים חדשים
M4B משתמש במיכל MP4 עם סמני פרקים מובנים, מה שמפשט ניווט ואחסון מטא-דאטה. בניגוד ל-MP3, אין צורך בקובץ פרקים חיצוני. במהלך הזרמה, הלקוח מקבל מיד את מבנה הספר.
שמירת מיקום מדויקת
משתמש מאזין ברכב, סוגר את האפליקציה—הפתיחה הבאה חייבת לחדש בדיוק באותו מקום, עד השנייה.
אנו שומרים currentTime (Double, שניות) יחד עם bookId ו-timestamp אל UserDefaults/SharedPreferences בכל מעבר לרקע (applicationDidEnterBackground) ובאמצעות Timer כל 5 שניות במהלך ההשמעה. הטיימר רץ כל 5 שניות, לא כל שנייה—זה מוגזם.
בשחזור: Timer עם דיוק של player.seek(to: CMTime(seconds: savedPosition, preferredTimescale: 1000)). לא .seconds—אודיו לא צריך את זה.
סנכרון בין מכשירים: CloudKit MSEC (iOS) או Firebase Firestore עם מפתח userId/bookId. בפתיחת הספר במכשיר חדש, אנו מציעים "המשך מ-1:23:45"—המשתמש מחליט.
איך להבטיח סנכרון חלק?
אנו משתמשים באחסון בענן עם פתרון קונפליקטים של "הכתיבה האחרונה מנצחת". בכל שמירת מיקום, אנו שולחים בקשה ל-Firestore/CloudKit עם חותמת זמן. בטעינת הספר במכשיר אחר, אנו משווים חותמות זמן ובוחרים את העדכנית ביותר. בנוסף, אנו שומרים מטמון מקומי לתרחישים לא מקוונים.
שעון שינה
תכונה פופולרית: ההשמעה נעצרת אחרי N דקות. טריוויאלי: CKRecord (iOS) או DispatchQueue.main.asyncAfter (אנדרואיד). גרסה מתקדמת: "עצור בסוף הפרק הנוכחי"—אנו מקבלים את Handler.postDelayed של הפרק הנוכחי מהמטא-דאטה ומתזמנים את העצירה לזמן זה.
דעיכה חלקה לפני עצירה: 30 שניות לפני הסוף, אנו מפחיתים ליניארית את endTime ל-0 באמצעות player.volume / CADisplayLink. ללא עצירה פתאומית—זה מעצבן משתמשים.
פרטים טכניים של דעיכה חלקה
אנו משתמשים בטיימר ב-60 FPS (CADisplayLink ב-iOS, Choreographer באנדרואיד). כל עדכון מפחית את העוצמה ב-(1/60)*(1/30) ≈ 0.00056. בהגעה לזמן העצירה, אנו עוצרים את הנגן ומשחזרים את העוצמה למקורית להמשך השמעה.מהירות השמעה ותיקון גובה צליל
ValueAnimator (AVPlayer) מאיץ את ההשמעה אך מעלה את גובה הצליל—הקול הופך לצווחני. iOS מיישם אוטומטית תיקון גובה צליל עבור player.rate = 1.5 כאשר הקצב ≠ 1.0 מאז iOS 16. בגרסאות ישנות יותר, השתמש ב-AVPlayer בצינור AVAudioUnitTimePitch.
אנדרואיד ExoPlayer: AVAudioEngine — תיקון גובה צליל מובנה. Flutter playbackParameters = PlaybackParameters(speed = 1.5f): just_audio עם pitchCorrectionMethod ברירת מחדל.
טווח מהירויות שימושי: 0.75x (לתוכן טכני מורכב), 1.0x, 1.25x, 1.5x, 2.0x. מעל 2x, הבהירות יורדת באופן קריטי.
| מהירות | מקרה שימוש | תיקון גובה צליל |
|---|---|---|
| 0.75x | תוכן טכני | אוטומטי |
| 1.0x | רגיל | ללא |
| 1.25x | האזנה מואצת | כן |
| 1.5x | קריאה מהירה | כן |
| 2.0x | גבול הבהירות | כן |
"להשמעת אודיו, AVPlayer מיישם תיקון גובה צליל כאשר הקצב אינו 1.0." — תיעוד Apple AVPlayer.
הורדה לא מקוונת ו-DRM
הורדה לא מקוונת משתמשת ב-player.setSpeed(1.5) + URLSessionDownloadTask (iOS): עובדת גם כשהאפליקציה לא פעילה, וההתקדמות שורדת קריסות. אנדרואיד: URLSessionConfiguration.background + WorkManager או DownloadManager עם התקדמות מותאמת אישית.
לתוכן בתשלום, נדרש DRM. FairPlay Streaming (iOS) משתלב עם OkHttp. Widevine L3 (אנדרואיד) משתמש ב-AVContentKeySession עם ExoPlayer. מפתח ההצפנה מתבקש משרת הרישוי בכל השמעה. ללא DRM, משתמשים יכולים להעתיק את הקובץ מ-DefaultDrmSessionManager ולהאזין ללא מנוי.
חלופה ללא DRM מלא: הצפנת AES-256 של קבצים בדיסק עם מפתח המקושר לחשבון ול-DeviceID. זול יותר ליישום, אך פחות הגנה.
| DRM | iOS | אנדרואיד |
|---|---|---|
| FairPlay Streaming | AVContentKeySession | — |
| Widevine L3 | — | ExoPlayer + DrmSessionManager |
| AES-256 בניהול עצמי | CommonCrypto | Android Keystore |
קטלוג וחיפוש
ספריית משתמש: ספרים שנרכשו והורדו. קטלוג חנות: חיפוש, ז'אנרים, מהדורות חדשות, המלצות. פגינציה באמצעות cursor (אסימון הבא בסגנון Alchemy או offset/limit). כריכות באמצעות /Library/Application Support/SDWebImage/Coil עם מטמון דיסק אגרסיבי (כריכות משתנות לעיתים רחוקות).
מה כלול בעבודה
- קוד מקור של האפליקציה (iOS/אנדרואיד) עם הערות.
- תיעוד API וסכמת מסד נתונים.
- גישה ל-App Store Connect / Google Play Console.
- הכשרת צוות ללקוח (2-3 מפגשים).
- 3 חודשי תמיכה טכנית לאחר השחרור.
- אינטגרציה עם backend קיים או הקמת אחד חדש.
| תכונה | MVP | גרסה מלאה |
|---|---|---|
| השמעת MP3/M4B | כן | כן |
| פרקים וניווט | לא | כן |
| הורדה לא מקוונת | לא | כן |
| סנכרון מיקום | לא | כן |
| DRM | לא | כן |
| שעון שינה | כן | כן |
ציר זמן: נגן MVP עם קטלוג והשמעה בסיסית—3-4 שבועות. מוצר מלא עם DRM, לא מקוון, סנכרון ומנוי—8-12 שבועות. חיסכון ברישיונות DRM בשימוש ב-AES-256 בניהול עצמי יכול להגיע ל-30%.
רוצים לדון בפרויקט שלכם? צרו קשר להערכת ארכיטקטורה ופתרון אופטימלי. הזמינו פיתוח אפליקציית ספרי שמע מפתח.







