core icon indicating copy to clipboard operation
core copied to clipboard

Media Source API

Open goto-bus-stop opened this issue 6 years ago • 2 comments

Currently, media sources have three API methods:

  • search
  • get
  • import

search and get are quite clear and good now, but import is basically a wildcard and requires custom client support. This issue is intended to scope out the features that would be required in a more restrictive API.

Feature Checklist

  • [ ] Flagging support for the below features Probably just assign each a name. It could be implicit on the implementer end, so Core would determine it based on which methods are available.
  • [ ] Import a playlist from a URL Like the current YouTube source, on the web client this should open a panel that lists all the media in a playlist, and that has an "import all" button that can be used to import the entire thing into a new üWave playlist.
    • [ ] One feature that would be good to have that does not exist right now is pagination; this may be quite difficult though because not all third parties support the type of pagination that üWave needs.
  • [ ] Optionally, view playlists belonging to a user account / [other similar concept].
  • [ ] Optionally, view playlists of the current user (if account is connected).
    • How to do the account connection stuff?
  • [ ] ...
interface MediaSource {
  api?: number;
  name: string;

  get(context: SourceContext, sourceIDs: string[]): Promise<MediaItem[]>;
  search(context: SourceContext, query: string, pagination?: any): Promise<MediaItem[]>;

  // Get media items in a playlist. sourceID may be ID or a playlist URL or etc
  getPlaylistItems(context: SourceContext, sourceID: string): Promise<MediaItem[]>;
  // Get playlists owned by the user [throw error if user has no connected account]
  getSelfPlaylists(context: SourceContext): Promise<PlaylistMeta[]>;
  // Get (public) playlists owned by some other user or similar concept.
  getUserPlaylists(context: SourceContext, userID: string): Promise<PlaylistMeta[]>;
  // maybe?
  searchPlaylists(context: SourceContext, query: string): Promise<PlaylistMeta[]>;
}

goto-bus-stop avatar Oct 09 '18 13:10 goto-bus-stop

We're going to need an API rework to support runtime configuration. It's kind of possible already because media sources have access to the uw object, but it's unergonomic. You can't use the options parameter that uw.source() gives you because that is static.

For runtime configuration, sources should be managed more strictly by üWave Core. Maybe an API could look like this:

interface MediaItem {
  ...
}

interface PlaylistMeta {
  ...
}

// Maybe a generic parameter for the options object?
// Maybe a generic parameter for the SourceData?
abstract class MediaSource {
  static api: number = 3;
  static name: string;

  // This schema would automatically be merged with an `{ enabled: { type: boolean } }` schema using `allOf`
  static schema: JSONSchema;
  
  constructor(options: object /* or maybe a generic type */);

  get(context: SourceContext, sourceIDs: string[]): Promise<MediaItem[]>;
  search(context: SourceContext, query: string, pagination?: any): Promise<MediaItem[]>;

  // Get media items in a playlist. sourceID may be ID or a playlist URL or etc
  // optional
  getPlaylistItems(context: SourceContext, sourceID: string): Promise<MediaItem[]>;
  // Get playlists owned by the user [throw error if user has no connected account]
  // optional
  getSelfPlaylists(context: SourceContext): Promise<PlaylistMeta[]>;
  // Get (public) playlists owned by some other user or similar concept.
  // optional
  getUserPlaylists(context: SourceContext, userID: string): Promise<PlaylistMeta[]>;
  // maybe?
  // optional
  searchPlaylists(context: SourceContext, query: string): Promise<PlaylistMeta[]>;  

  // optional
  close(): Promise<void> | void {}
}

goto-bus-stop avatar Sep 04 '20 10:09 goto-bus-stop

Turns out static abstract members are not a thing. That was the reason for using an abstract class in the sketch above instead of an interface.

Perhaps it could be an interface and a @u-wave/media-source function that wraps it:

import { createMediaSource, MediaSource } = require('@u-wave/media-source');

class YouTubeSource implements MediaSource {
  // ...
}

export = createMediaSource(YouTubeSource, {
  name: 'youtube',
  schema: {
    // ...
  },
})

goto-bus-stop avatar Sep 05 '20 13:09 goto-bus-stop