Giter Site home page Giter Site logo

relistennet / relisten-ios Goto Github PK

View Code? Open in Web Editor NEW
36.0 36.0 14.0 182.06 MB

An iOS app for streaming millions of free music recordings from the Internet Archive

Home Page: http://relisten.net

License: MIT License

Swift 34.07% Ruby 0.52% Objective-C 64.92% Shell 0.22% Python 0.28%
hacktoberfest

relisten-ios's People

Contributors

alecgorge avatar cpaine-ts avatar devsaider avatar farktronix avatar lukeswitz avatar snyk-bot avatar switz avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

relisten-ios's Issues

Track durations occasionally get formatted with the wrong zeroFormattingBehavior

I'm seeing track durations occasionally show up like '05:40' or '2:0'. The duration usually looks fine if I go back out of the show view and back in, so this is a race condition somewhere when formatting the string.

DateComponentsFormatter claims to be thread-safe but when I add a serial queue to the humanize() function this bug goes away.

The serial queue is a good band aid for now, but I'd like to get to the bottom of why this race is happening, and I'll use this bug report to track that.

App hangs with layout errors after favoriting Bela Fleck and the Flecktones

I added Bela Fleck and the Flecktones to my favorite artists and the app hung with this error and stack trace:

2018-08-10 16:48:53.973308-0700 Relisten[33346:8463063] [Collection] reloadData <ASCollectionNode: 0x7f94a15072a0>
2018-08-10 16:48:53.973675-0700 Relisten[33346:8463063] [Collection] New content: { itemCounts = [ <S0: 25> ] }
2018-08-10 16:48:53.989485-0700 Relisten[33346:8463063] The behavior of the UICollectionViewFlowLayout is not defined because:
2018-08-10 16:48:53.989619-0700 Relisten[33346:8463063] the item height must be less than the height of the UICollectionView minus the section insets top and bottom values, minus the content insets top and bottom values.
2018-08-10 16:48:53.989712-0700 Relisten[33346:8463063] Please check the values returned by the delegate.
2018-08-10 16:48:53.990370-0700 Relisten[33346:8463063] The relevant UICollectionViewFlowLayout instance is <UICollectionViewFlowLayout: 0x7f94a1508890>, and it is attached to <ASCollectionView: 0x7f94a1a49600; baseClass = UICollectionView; frame = (0 0; 375 120); clipsToBounds = YES; gestureRecognizers = <NSArray: 0x60c0006530b0>; layer = <ASCollectionNode-Layer: 0x60c000642be0; node = <ASCollectionNode: 0x7f94a15072a0>>; contentOffset: {0, 0}; contentSize: {5263.9999999999991, 120}; adjustedContentInset: {0, 0, 0, 0}> collection view layout: <UICollectionViewFlowLayout: 0x7f94a1508890>.
2018-08-10 16:48:53.990464-0700 Relisten[33346:8463063] Make a symbolic breakpoint at UICollectionViewFlowLayoutBreakForInvalidSizes to catch this in the debugger.
(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 2.1
  * frame #0: 0x000000010962ad84 UIKit`UICollectionViewFlowLayoutBreakForInvalidSizes
    frame #1: 0x000000010962e9de UIKit`-[_UIFlowLayoutSection logInvalidSizesForHorizontalDirection:warnAboutDelegateValues:] + 219
    frame #2: 0x000000010962e8f0 UIKit`-[_UIFlowLayoutSection logInvalidSizes] + 72
    frame #3: 0x0000000109630c34 UIKit`-[_UIFlowLayoutSection computeLayoutInRect:forSection:invalidating:invalidationContext:] + 7606
    frame #4: 0x00000001095b1536 UIKit`__76-[UICollectionViewFlowLayout _updateItemsLayoutForRect:allowsPartialUpdate:]_block_invoke + 533
    frame #5: 0x000000010bc3cb4e CoreFoundation`-[__NSArrayM enumerateObjectsWithOptions:usingBlock:] + 222
    frame #6: 0x00000001095b119b UIKit`-[UICollectionViewFlowLayout _updateItemsLayoutForRect:allowsPartialUpdate:] + 495
    frame #7: 0x00000001095a9b5c UIKit`-[UICollectionViewFlowLayout invalidateLayoutWithContext:] + 1331
    frame #8: 0x000000010959ed90 UIKit`-[UICollectionViewLayout _invalidateLayoutUsingContext:] + 63
    frame #9: 0x000000010955f495 UIKit`-[UICollectionView _invalidateLayoutIfNecessaryForReload] + 155
    frame #10: 0x000000010955e717 UIKit`-[UICollectionView reloadData] + 985
    frame #11: 0x0000000105a47f47 AsyncDisplayKit`::__64-[ASCollectionView rangeController:updateWithChangeSet:updates:]_block_invoke(.block_descriptor=0x00007ffeeacadc48) at ASCollectionView.mm:2131
    frame #12: 0x0000000105a47e8b AsyncDisplayKit`ASPerformBlockWithoutAnimation(withoutAnimation=NO, block=0x0000000105a47ec0) block_pointer) at ASInternalHelpers.h:92
    frame #13: 0x0000000105a47b63 AsyncDisplayKit`::-[ASCollectionView rangeController:updateWithChangeSet:updates:](self=0x00007f94a1a49600, _cmd="rangeController:updateWithChangeSet:updates:", rangeController=0x000060c0002ad4a0, changeSet=0x00006040005a6c80, updates=0x0000000105a61950) at ASCollectionView.mm:2126
    frame #14: 0x0000000105b5562d AsyncDisplayKit`::-[ASRangeController dataController:updateWithChangeSet:updates:](self=0x000060c0002ad4a0, _cmd="dataController:updateWithChangeSet:updates:", dataController=0x000060c000137e80, changeSet=0x00006040005a6c80, updates=0x0000000105a61950) at ASRangeController.mm:521
    frame #15: 0x0000000105a618d6 AsyncDisplayKit`::__40-[ASDataController updateWithChangeSet:]_block_invoke_2.224(.block_descriptor=0x000060800107ca00) at ASDataController.mm:610
    frame #16: 0x0000000105b29d94 AsyncDisplayKit`::__30-[ASMainSerialQueue runBlocks]_block_invoke(.block_descriptor=0x00006080006527e0) at ASMainSerialQueue.mm:73
    frame #17: 0x000000010dd407ab libdispatch.dylib`_dispatch_call_block_and_release + 12
    frame #18: 0x000000010dd417ec libdispatch.dylib`_dispatch_client_callout + 8
    frame #19: 0x000000010dd4c8cf libdispatch.dylib`_dispatch_main_queue_callback_4CF + 628
    frame #20: 0x000000010bc72c99 CoreFoundation`__CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ + 9
    frame #21: 0x000000010bc36ea6 CoreFoundation`__CFRunLoopRun + 2342
    frame #22: 0x000000010bc3630b CoreFoundation`CFRunLoopRunSpecific + 635
    frame #23: 0x000000010f573a73 GraphicsServices`GSEventRunModal + 62
    frame #24: 0x0000000108a9f057 UIKit`UIApplicationMain + 159
    frame #25: 0x0000000104f684c7 Relisten`main at RLAppDelegate.swift:20
    frame #26: 0x000000010ddbe955 libdyld.dylib`start + 1
    frame #27: 0x000000010ddbe955 libdyld.dylib`start + 1

Too many blank trays

The show trays on the artists view controller are frequently showing up with no cells inside of them.

Relisten Roadmap

Relisten Roadmap

This is on the iOS repo for now even though it covers more products.

Relisten 4.0, PhishOD 5.0

Release target: 2018-09-01. Beta release target: 2018-08-25.

iOS

Release focal points

  • Rock-solid playback
  • Rock-solid offline support
  • CarPlay
  • Making the App Store version of Relisten and PhishOD operate in lockstep

Other features

  • API based browsing by years, venues, songs, top, random
  • “Recently Played” shows available by artist and by all artists. This will not sync between devices
  • Deep-linking from relisten.net works properly to encourage sharing
  • Button to remove all downloaded tracks for a show
  • Method for people to provide feedback to [email protected] or get a FOSS license to Instabug
  • Crashlytics
  • Fastlane

Web

Release focal points

  • Stabilize gapless playback in Firefox
  • Allow editing and reordering of now-playing queue
  • View artist: top shows, venues and songs

Sonos

Release focal points

  • View artist: top shows, venues and songs

Relisten 4.1, PhishOD 5.1

Release target: 2018-10-01. Beta-release target: 2018-09-24.

Discussion points

  • Should we support emails as well for doing 1-time password authentication?
  • Should we allow people to build their library like Spotify, adding individual tracks or stick to complete shows?
  • Do people add specific sources to their library or a show including all sources?

API

Release focal points

  • Upgrade to Postgres 10
  • Rigorous database backups to S3 (or Dropbox)
  • User authentication via phone number + SMS
  • Users can build their own “library”
    • Favorite shows
    • Favorite artists

iOS

Release focal points

  • Add UI for users to add Favorite Artists and Shows to their Library
  • Allow users to sign in/up when interacting with library UI

Other features

  • ???

Web

Release focal points

  • Add UI for users to add Favorite Artists and Shows to their Library
  • Allow users to sign in/up when interacting with library UI

Sonos

Release focal points

  • Allow users to sign in/up and view favorite artists + library

Relisten 4.2, PhishOD 5.2

Release target: 2018-XX-XX. Beta-release target: 2018-XX-XX.

Discussion Points

  • Playlist support across iOS, web and Sonos—including sharing
  • Adding someone else’s (read-only) playlist to your library
  • Duplicating playlists
  • tvOS support

Relisten crashes when launched from CarPlay if the device is locked

If Relisten isn't running and I launch it from CarPlay the app crashes on launch. I'm pretty sure this is due to BASS not being ok with being initialized while the device is locked, but I can't get symbols out of this crash report.
This is simple enough to reproduce, but I'm going to have to sit in my parked car looking like a dork while debugging this.

CarPlay controller often loads data for the wrong artist

I've noticed that when I use CarPlay and my network connection is slow I'll tap to load something and I'll get results for a different artist than I tapped on.

I'm pretty sure this is happening because the controller is getting an update from Siesta (or maybe Firebase favorites loading) and updating the artist array, which bumps the indices of everything, confusing CarPlay.

I'll need to add some locking around what's currently being displayed, and hold any updates until the user pops back up the navigation stack.

Improve download buttons UX

  • The download a complete show should prompt before downloading
  • Test canceling/deleting a whole show while it is downloaded
  • Should the download button have text above it to make clear that it is a download button? Do we need a different button?
  • You should be able to download the whole show if you only have part of it downloaded
  • You should be able to delete everything that is downloaded for the show

Favorites are getting re-imported every launch

While watching the logs I noticed that my favorites are getting re-imported from SDCloudUserDefaults every time the app launches. The import code is clearing out the favorites and saving them back to SDCloudUserDefaults so I'm not sure why they keep coming back.

Synchronize downloading indicators

Either (a) we need to show download percentage (not worth it imo) or (b) make it so the flashing happens at the same time. I think the easiest way is to try to time everything to start to the nearest .5 or .25 second using the delay part of UIAnimation. Low priority though

Recommended Shows Plan

Most of the time when I launch Relisten I'm ready to listen to some music, but unless I'm currently in the middle of a show I don't quite know what I want to listen to.

I'd like to add a list of recommended shows on the home screen based on the user's listening history to make it easier to launch Relisten and jump right into listening to something new.

First, a mockup:
simulator screen shot - simulator x - 2018-08-02 at 16 55 39

My Stuff

Recently Played Tracks

Right now, Relisten stores the 25 most recently played tracks, and it only counts a track as recently played if you pass the halfway mark of the track. This can be problematic if you start listening to a show and only make it a couple minutes in to a song and then get interrupted. It's also a bit confusing, since I would expect a track to show up as recently played as soon as I start listening to it.

Relisten should store the most recently played track immediately on playback start, but it should only store one recently played track per artist.

Listening History

Relisten should keep a history of every track that has been listened to. Only marking as listened after the halfway point makes sense for considering a song completely listened to (like publishing to last.fm and phishtrackstats.

This history can potentially end up being a large number of small entries, so a sqlite database seems like the best fit for this. Relisten should be able to quickly query for things like:

  • What is the most recently listened to but incomplete show for an artist?
  • Has a show been completely listened to?
  • How many tracks out of a show have been listened to
  • How many tracks for an artist have been listened to

Discover

Recently Performed

If an favorited artist has performed a show within the last month, the most recent show should appear in the recommended shows list.

Top Shows/On This Day/Random

The rest of the discover list should be populated with:

  • Top rated shows
  • Shows from on this day
  • Random shows
    Shows should be picked that haven't been completely listened to by the user. One or two of the items above per artist should be picked to fill in the rest of the list.

Favorite Artists

For the purposes of these sections, favorite artists should be any artist the user has explicitly favorited, but it should also include any artist that the user seems to be listening to more than casually right now. If the user has listened to 20 or more complete tracks from an artist in the last month then they should be included in the favorite artists list for picking recommended shows.

Offline Mode

The "Available Offline" section can be dropped from the main screen, since favorite or recently played shows are more important most of the time. However, there's one time when showing it is very helpful, and that's when the user is offline.

If Relisten detects that it can't reach the Relisten API servers then the My Stuff section should be filled with offline shows sorted in this order:

  1. Most recently played (one show per artist)
  2. Download date

Crash on launch due to items in ArtistsViewController changing outside the update

I hit this crash on launch just once. It looks like it's a race condition with reloading the data in the ArtistsViewController

2018-08-10 16:46:17.526039-0700 Relisten[33242:8458741] Invalid number of items in section 5. The number of items after the update (2) must be equal to the number of items before the update (0) plus or minus the number of items inserted or deleted (0 inserted, 0 deleted).

Backtrace:

(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
    frame #0: 0x0000000110be4001 libobjc.A.dylib`objc_exception_throw
    frame #1: 0x00000001115c4975 CoreFoundation`+[NSException raise:format:] + 197
    frame #2: 0x000000010b2feff2 AsyncDisplayKit`::-[ASDataController updateWithChangeSet:](self=0x0000604000127620, _cmd="updateWithChangeSet:", changeSet=0x00006080001af260) at ASDataController.mm:561
    frame #3: 0x000000010b44372f AsyncDisplayKit`::-[ASTableView endUpdatesAnimated:completion:](self=0x00007fb453834400, _cmd="endUpdatesAnimated:completion:", animated=YES, completion=(null))(BOOL)) at ASTableView.mm:718
    frame #4: 0x000000010b44310a AsyncDisplayKit`::-[ASTableView endUpdatesWithCompletion:](self=0x00007fb453834400, _cmd="endUpdatesWithCompletion:", completion=(null))(BOOL)) at ASTableView.mm:699
    frame #5: 0x000000010b44307f AsyncDisplayKit`::-[ASTableView endUpdates](self=0x00007fb453834400, _cmd="endUpdates") at ASTableView.mm:693
    frame #6: 0x000000010b444637 AsyncDisplayKit`::-[ASTableView reloadSections:withRowAnimation:](self=0x00007fb453834400, _cmd="reloadSections:withRowAnimation:", sections=1 index, animation=UITableViewRowAnimationAutomatic) at ASTableView.mm:801
    frame #7: 0x000000010b43a67f AsyncDisplayKit`::-[ASTableNode reloadSections:withRowAnimation:](self=0x00007fb450421230, _cmd="reloadSections:withRowAnimation:", sections=1 index, animation=UITableViewRowAnimationAutomatic) at ASTableNode.mm:730
  * frame #8: 0x000000010a7fa4d8 Relisten`closure #1 in ArtistsViewController.resourceChanged(resource=0x00007fb450558350, self=0x00007fb45400b200) at ArtistsViewController.swift:205
    frame #9: 0x000000010a7fab01 Relisten`partial apply for closure #1 in ArtistsViewController.resourceChanged(_:event:) at ArtistsViewController.swift:0

Add credits somewhere

Credit needs to be given for the icons from the Noun Project
I'm guessing some of the projects used via Cocoapods also require (or at least deserve) credit.

Relisten should have a credits view somewhere- since there's not in-app settings right now maybe the best place is in the per-app stuff in Settings.app?

Background audio stops as soon as PhishOD launches

If another application is playing audio when PhishOD launches that audio stops when BASS_Init() is called. The call stack leading to that is:

  * frame #0: BASSGaplessAudioPlayer`-[ObjectiveBASS setupBASS](self=0x00007f8753241e00, _cmd="setupBASS") at ObjectiveBASS.m:277
    frame #1: BASSGaplessAudioPlayer`-[ObjectiveBASS init](self=0x00007f8753241e00, _cmd="init") at ObjectiveBASS.m:266
    frame #2: AGAudioPlayer`-[AGAudioPlayer setupBASS](self=0x000060000047a540, _cmd="setupBASS") at AGAudioPlayer.m:368
    frame #3: AGAudioPlayer`-[AGAudioPlayer setup](self=0x000060000047a540, _cmd="setup") at AGAudioPlayer.m:61
    frame #4: AGAudioPlayer`-[AGAudioPlayer initWithQueue:](self=0x000060000047a540, _cmd="initWithQueue:", queue=0x000060c00023b9a0) at AGAudioPlayer.m:55
    frame #5: Relisten`@nonobjc AGAudioPlayer.init(queue:) at PlaybackController.swift:0
    frame #6: Relisten`AGAudioPlayer.__allocating_init(queue:) at PlaybackController.swift:0
    frame #7: Relisten`PlaybackController.init() at PlaybackController.swift:38
    frame #8: Relisten`PlaybackController.__allocating_init() at PlaybackController.swift:0
    frame #9: Relisten`globalinit_33_CEB0CB2109270705D0E6647B36802A82_func1 at PlaybackController.swift:34
    frame #10: libdispatch.dylib`_dispatch_client_callout + 8
    frame #11: libdispatch.dylib`dispatch_once_f + 285
    frame #12: Relisten`PlaybackController.sharedInstance.unsafeMutableAddressor at PlaybackController.swift:34
    frame #13: Relisten`AppDelegate.setupPlayback(self=0x0000608000226880) at RLAppDelegate.swift:79
    frame #14: Relisten`AppDelegate.application(application=0x00007f8752700ac0, launchOptions=nil, self=0x0000608000226880) at RLAppDelegate.swift:69
    frame #15: Relisten`@objc AppDelegate.application(_:didFinishLaunchingWithOptions:) at RLAppDelegate.swift:0

Phish artist view doesn't show recent shows

The horizontal show views for On This Date and Recently Played in the Phish artist view doesn't appear, even though they have shows loaded when I view them in the debugger.

Recent tracks JSON quickly exceeds the Firestore document size limit

I noticed that my recent tracks were disappearing shortly after they were played. They'd show up in the recent tracks view, but then they'd disappear a few seconds later.

I finally chased this down to Firestore refusing the save of the library and silently reverting back to the server's data:

2018-07-09 16:14:10.387710-0700 Relisten[10129:315605] 5.0.0 - [Firebase/Firestore][I-FST000001] FSTWriteStream 0x60800011a280 close: Error Domain=FIRFirestoreErrorDomain Code=3 "A document cannot be written because it exceeds the maximum size allowed."

I just have 12 recently played tracks, but it looks like the JSON for each track is pretty big, since it contains all of the artist/show/source information, including every track in the show. Those 12 tracks end up being over 1MB of JSON data.

Improve CarPlay artwork performance

Artwork was disabled in CarPlay due to slow performance- loading the shows for a year, then going back to the year view, then back in to shows would sometimes take upwards of two minutes for the view to present.

Let's get to the bottom of the performance and re-enable artwork in CarPlay.

Add settings

Relisten needs a settings view. This can also contain the credits needed for #84. It should contain some of the same stuff as the previous version- links to GitHub and version information.

For now the only other setting is to enable/disable shake to report a bug. We can also just turn off shake to report and have a button in settings to file a bug that jumps to the GitHub issues page.

New persistence model

Note: this is quite rough and thought of late a night. This will require revisions:

Relisten Persistence Model

All objects eligble for persistence have a uuid property.

Is there a way that we can make persistence generic? How much storage is too much storage?

Objects with a uuid property vary in size. Some are consistent and small, like an Artist. Some objects can be large or small—such as ShowWithSources. The number of sources within the show or the corresponding descriptions can vary wildly from show to show—even within the same artist.

However, in theory, this is not a problem. Even a very large show with many sources isn’t completely unwieldy since the largest variable (reviews) is behind a separate API request now.

What we are really talking about is a difference between a couple hundred bytes and a couple kilobytes. Does this make a substantial difference on a modern iPhone? Let’s do the math, Fermi estimation style:

A true fan of a band might have 100 shows favorited. A truly rediculous show like Grateful Dead’s 5/8/1978 is a 281 KiB raw JSON response, but for a Fermi estimation let’s call it 200 KiB of resident memory. This means a wild fan of a band would need 200 KiB * 100 = 20,000 KiB = 20 MiB of memory to hold all of their favorite shows within instant reach.

To be frank, 20 MiB isn’t a ton of memory and it may be premature optimization to break things down further/smaller than that. Even if we held twice as many objects in memory—which is more shows than many bands have played—we are still safely in a range where even a power-user on paltry iPhone 5S with 1GiB of RAM would be happy. We can’t give up 20 MiB here and 20 MiB there and come out with an acceptable memory usage (despite being ill-defined) but cutting out Firebase completely gives us a chunk of memory back (exact amount unknown).

Summary: Just hold all the shows in memory, but disk backed for persistence—it is easier and doesn’t enough memory to be even a minor issue.

Okay, so we are holding all the shows in memory with a disk backing...how does that actually happen?

Siesta provides a really cool pipeline stage called cleanup that happens after the JSON response has been parsed into models. We can hook into this stage of the pipeline only on routes that return “persistent” models (ones with a uuid) using configure(whenURLMatches:requestMethods:description:configurer:).

We can use this to automatically store any ShowWithSources into different disk backed caches with 50 MiB automatically held in memory using Cache.

We can write to the cache anytime we receive a response and read from it whenever we want. Then, to maintain a “favorites” list we just have to store a list of uuids and hydrate that from the cache whenever we need to access it. In general, the favorited shows should stay in memory and be quick to access. In the worst case, we need to go to the SSD and fetch it.

Because we do not sync, something can only be in favorites if it has been accessed on that device and we do not need to worry about cache misses which fetching the list of favorites. Of course, it is prudent to still handle (aka skip that show) this scenario despite it being “impossible” like so many programming bugs.

We should not hold an array of all the shows in memory that gets invalidated any time a show is favorited/un-favorited. This would double memory usage for no real benefit. We can assemble the whole list of shows from only the uuids with very little overhead despite it feeling “wrong”. It might not be the most optimal, but it is great for our purposes because:

  1. It is easy to reason about
  2. This leads to less bugs
  3. This leads to less maintence

Yay open source.


API

Data Stores

  • Cache for all offline data
    • Main holds ShowWithSources entires with a key of show_uuid
    • Has a special entry called __tracks$$ that holds a [String: (show_uuid: String, source_uuid: String)] mapping. This is held in memory manually and is persisted on every mutation.
    • Has a special entry called __sources$$ that holds a [String: String] mapping from source UUIDs to show UUIDs
    • Has a special entry called __favoriteShows$$ that holds a [FavoriteShow]
    • Has a special entry called __favoriteSources$$ that holds a [FavoriteSource]
    • Has a special entry called __favoriteTracks$$ that holds a [FavoriteTrack]
    • Has a special entry called __playbackQueue$$ that holds a PlaybackResortation for restoring playback on launch

Models

struct PlaybackRestoration {
	public let queue: [UUID] = [] // track UUIDs
	public let active: UUID? = nil // active track UUID
	public let playbackPosition: TimeInterval? = nil // position in active track
}

struct FavoriteShow {
	public let show_uuid: UUID
	public let created_at: Date
}

struct FavoriteSource {
	public let source_uuid: UUID
	public let created_at: Date
}

struct FavoriteTrack {
	public let track_uuid: UUID
	public let created_at: Date
}

struct SourceFullInShowWithSources {
	public let show: ShowWithSources
	public let source: SourceFull
}

struct SourceTrackInSourceFullInShowWithSources {
	public let show: ShowWithSources
	public let source: SourceFull
	public let track: SourceTrack
}

Methods

  • show(byUUID: UUID) -> ShowWithSources
    • Simple, straight backing-cache lookup
  • source(byUUID: UUID) -> SourceFullInShowWithSources
    • Uses __sources$$ to lookup the show uuid
    • Uses the backing cache to pull full information
    • Finds the matching source within
  • show(forSourceUUID uuid: UUID) -> ShowWithSources
    • source(byUUID: uuid).show
  • show(byTrackUUID uuid: UUID) -> ShowWithSources
    • track(byUUID: uuid).show
  • source(byTrackUUID uuid: UUID) -> SourceFull
    • track(byUUID: uuid).source
  • track(byUUID uuid: UUID) -> SourceTrackInSourceFullInShowWithSources
    • Uses __tracks$$ to find the show uuid
    • Uses the backing cache to pull full information
    • Filters to find the matching source
  • save(show: ShowWithSources)
    • Adds an entry to __tracks$$ for every track in every set
    • Adds an entry to __sources$$ for every source
  • addFavorite(show: Show, in show: ShowWithSources)
    • Adds an entry to __favoriteShows$$
    • calls save(show: show)
  • addFavorite(source: Source, in show: ShowWithSources)
    • Adds an entry to __favoriteSources$$
    • calls save(show: show)
  • addFavorite(track: SourceTrack, in show: ShowWithSources)
    • Adds an entry to __favoriteTracks$$
    • calls save(show: show)
  • removeFavorite(_ uuid: UUID) -> Bool
    • Removes from any of the favorite arrays, stopping on the first successful removal
  • savePlaybackRestorationInformation(_ info: PlaybackRestoration)
  • public let favoriteShows: Observable<[ShowWithSources]>
  • public let favoriteSources: Observable<[SourceFullInShowWithSources]>
  • favoriteSourcesIncludingIndirect() -> [SourceFullInShowWithSources]
    • Any direct favorites + any sources from favorite shows
  • public let favoriteTracks: Observable<[SourceTrackInSourceFullInShowWithSources]>
  • favoriteTracksIncludingIndirect() -> [SourceTrackInSourceFullInShowWithSources]
    • Any direct favorites + any tracks from favorite sources + any tracks from favorite show

Migration to user based API in future

Easy here because the special keys are just cached version of things fetched on the server. Updated before any change—like git pull --rebase origin master—then change is applied. Also updated on any app open. No offline support for modifying favorites.

Beta?

How can I opt into the beta? Is there a testflight?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.