Skip to content

Instantly share code, notes, and snippets.

@bswags
Last active January 8, 2021 21:43
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bswags/6200bd1117975c8457d87b196735cc70 to your computer and use it in GitHub Desktop.
Save bswags/6200bd1117975c8457d87b196735cc70 to your computer and use it in GitHub Desktop.
Initial thoughts on Mixtape spec
// Various rough thoughts around schemas and datatypes for Project Mixtape.
// Placeholders from existing for TS help.
type User = {};
// I think these can basically be user preview objects.
type GroupMember = Partial<User>;
// User previews with metadata for presence, status, reactions, etc.
type SessionMember = User;
type SessionInvite = {};
/**
* TRACKS, MIXTAPES, ARTISTS, ALBUMS
*/
/**
* Audio tracks. We need to flex across different types and providers here.
*/
enum AudioType {
Music = 'music',
Podcast = 'podcast',
Audiobook = 'audiobook',
}
// In future, will contain Amazon, SoundCloud, etc.
enum AudioProvider {
Spotify = 'spotify',
Apple = 'apple',
ListenNotes = 'listenNotes',
}
// Platform-specific metadata for a track.
// Will be populated for 1 or more platforms depending on how well we map.
interface AudioTrackMetadata {
trackId: string;
provider: AudioProvider;
uri: string; // Identifier on the 3rd party provider.
meta: any; // Can be stored as JSONB in Postgres/Cloud SQL. See: https://www.compose.com/articles/faster-operations-with-the-jsonb-data-type-in-postgresql/.
// ...other metadata we need to surface.
}
// Note: Keeping artist and album as simple as possible for now.
// It's possible we should capture metadata for the providers here as well,
// so we know e.g. what an artist's URI is on Spotify as well as Apple Music.
interface Artist {
id: string; // Native to Roadtrip.
name: string;
bio: string;
imageUrl: string;
thumbnailUrl: string;
rank: number; // Global rank by streams on Roadtrip.
topListeners: User[]; // Might only compute top 5-10.
plays: number; // Times their songs have been *played*.
streams: number; // Streams are plays x listeners in the session.
}
interface Album {
id: string; // Native to Roadtrip.
name: string;
releaseDate: string; // ISO timestamp.
trackCount: number;
imageUrl: string;
thumbnailUrl: string;
}
// We have an existing model that *mostly* works well here.
// See: https://github.com/roadtrip-fm/roadtrip-ios/blob/master/Roadtrip/Models/AudioTrack.swift
interface AudioTrack {
id: string; // Roadtrip's unique identifier for the track.
type: AudioType; // Allows us to split mixtapes by content type for now.
name: string;
album: Album;
artists: Artist[]; // 1:many via join table.
duration: number;
discNumber: number;
trackNumber: number;
rank: number; // Global song leaderboard? TBD.
plays: number;
streams: number;
}
// When a track is added to a mixtape we need its placement and attribution.
interface MixtapeTrack extends AudioTrack {
addedBy: User;
addedAt: string; // ISO timestamp.
suggestedBy?: User;
suggestedAt?: string; // ISO timestamp.
placement: number;
mixtapePlays: number; // Count of times played on this mixtape.
mixtapeStreams: number; // Count of times streamed on this mixtape.
upvotes: number; // TODO: This could be fun.
}
// Mixtapes are editable by the creator.
//
// TODO: Should we have a separate "Playlist" construct?
// Might be unnecessary, but might help define the divide between
// someone's playlist and their post, for referencing the actual
// playlist in a live session or remix scenario.
interface Mixtape {
id: string;
shortId: string;
creator: User;
name: string;
caption: string; // Creator caption (like a comment)
description: string; // Playlist description.
imageUrl?: string;
thumbnailUrl?: string;
type: AudioType; // Enforces single type per mixtape.
provider: AudioProvider; // Enforces single provider per mixtape.
tracks: MixtapeTrack[]; // Can materialize fields like duration, track count.
collaborative: boolean; // Not sure if supported for MVP.
likes: number;
comments: number; // Need to define comment structure.
shares: number; // Might not expose in UI.
plays: number;
streams: number;
remixSourceId?: string; // Track remixes.
createdAt: string; // ISO timestamp.
updatedAt: string; // ISO timestamp.
}
/**
* LIVE SESSIONS
*/
// Adapted from Room entities.
//
// A session for now will be tied to the mixtape's provider, so that
// only users who e.g. have Spotify can join a Spotify mixtape's sessions.
interface LiveSession {
id: string;
shortId: string;
host: User; // Always need one.
creator: User;
name?: string; // Custom names.
imageUrl?: string; // Custom images.
invites: SessionInvite[];
members: SessionMember[];
mixtape?: Mixtape // Current "playlist" or none for "Just Chatting".
visibility: 'everyone' | 'friends' | 'private';
currentTrack?: AudioTrack;
audioState: {}; // Encapsulation of necessary audio and sync state.
}
// I actually like the Twitch-style text-only messages for this.
// We had them in an old version and they felt snappy/lightweight.
interface LiveSessionMessage {
id: string;
sender: User;
text: string;
timestamp: string; // ISO timestamp.
}
/**
* MESSAGING
*/
enum MessageType {
Text = 'text',
Track = 'track',
Mixtape = 'mixtape',
Audio = 'audio',
Image = 'image',
Link = 'link',
}
interface MessageMention {
id: string;
type: 'member';
}
// Adapted from our current messages.
interface Message {
id: string;
type: MessageType;
text?: string;
sender: User;
contentId?: string; // ID of a track or mixtape.
contentUrl?: string; // URL of an audio file or image.
mentions?: MessageMention[];
createdAt: string; // ISO timestamp.
updatedAt: string; // ISO timestamp.
}
// Groups are containers for messaging right now.
//
// TODO: Cool to surface "live" members and call out sessions.
// This can be done by augmenting the members with online information.
interface Group {
id: string;
name?: string; // Custom name set by users.
members: GroupMember[];
imageUrl?: string; // Custom image set by users.
createdAt: string; // ISO timestamp.
updatedAt: string; // ISO timestamp.
latestMessage?: Message;
}
@blainekasten
Copy link

re: https://gist.github.com/bswags/6200bd1117975c8457d87b196735cc70#file-mixtape-ts-L104

My gut is that we should call these mixtapes but model them as Posts, because we will likely want to experiment with other types of posts. So Post is probably just a polymorphic array to different models, a Mixtape being one of them

@blainekasten
Copy link

re: https://gist.github.com/bswags/6200bd1117975c8457d87b196735cc70#file-mixtape-ts-L113

That feels like a bug more than a feature? I think it would be cool to mix a podcast with some songs. A really easy example is a church service podcast with some associated music for that message

@blainekasten
Copy link

Mixtape.shares, instead of a counter should it be relational? Help us build the user graph, 10 users shared your post, you may be interested in their posts, etc

@blainekasten
Copy link

// A session for now will be tied to the mixtape's provider, so that
// only users who e.g. have Spotify can join a Spotify mixtape's sessions.

I'm guessing this is until we can nail down our syncing logic to connect a spotify uri to a apple music uri to etc..?

@colevoss
Copy link

colevoss commented Jan 8, 2021

This is all looking really good and gives me a great head start on back end design

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment