• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

u-wave / core / 11980840475

22 Nov 2024 10:04PM UTC coverage: 78.492% (-1.7%) from 80.158%
11980840475

Pull #637

github

goto-bus-stop
ci: add node 22
Pull Request #637: Switch to a relational database

757 of 912 branches covered (83.0%)

Branch coverage included in aggregate %.

2001 of 2791 new or added lines in 52 files covered. (71.69%)

9 existing lines in 7 files now uncovered.

8666 of 11093 relevant lines covered (78.12%)

70.72 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

91.27
/src/Source.js
1
import { SourceNoImportError } from './errors/index.js';
1✔
2

1✔
3
/**
1✔
4
 * @typedef {import('./schema.js').User} User
1✔
5
 * @typedef {import('./schema.js').Playlist} Playlist
1✔
6
 * @typedef {import('./plugins/playlists.js').PlaylistItemDesc} PlaylistItemDesc
1✔
7
 */
1✔
8

1✔
9
/**
1✔
10
 * @typedef {{
1✔
11
 *   sourceType: string,
1✔
12
 *   sourceID: string,
1✔
13
 *   sourceData: import('type-fest').JsonObject | null,
1✔
14
 *   artist: string,
1✔
15
 *   title: string,
1✔
16
 *   duration: number,
1✔
17
 *   thumbnail: string,
1✔
18
 * }} SourceMedia
1✔
19
 */
1✔
20

1✔
21
/**
1✔
22
 * @typedef {object} SourcePluginV1
1✔
23
 * @prop {undefined|1} api
1✔
24
 * @prop {(ids: string[]) => Promise<SourceMedia[]>} get
1✔
25
 * @prop {(query: string, page: unknown, ...args: unknown[]) => Promise<SourceMedia[]>} search
1✔
26
 * @prop {(context: ImportContext, ...args: unknown[]) => Promise<unknown>} [import]
1✔
27
 * @typedef {object} SourcePluginV2
1✔
28
 * @prop {2} api
1✔
29
 * @prop {(context: SourceContext, ids: string[]) => Promise<SourceMedia[]>} get
1✔
30
 * @prop {(
1✔
31
 *   context: SourceContext,
1✔
32
 *   query: string,
1✔
33
 *   page: unknown,
1✔
34
 *   ...args: unknown[]
1✔
35
 * ) => Promise<SourceMedia[]>} search
1✔
36
 * @prop {(context: ImportContext, ...args: unknown[]) => Promise<unknown>} [import]
1✔
37
 * @prop {(context: SourceContext, entry: PlaylistItemDesc) =>
1✔
38
 *     Promise<import('type-fest').JsonObject>} [play]
1✔
39
 * @typedef {SourcePluginV1 | SourcePluginV2} SourcePlugin
1✔
40
 */
1✔
41

1✔
42
/**
1✔
43
 * Data holder for things that source plugins may require.
1✔
44
 */
1✔
45
class SourceContext {
1✔
46
  /**
1✔
47
   * @param {import('./Uwave.js').default} uw
1✔
48
   * @param {Source} source
1✔
49
   * @param {User} user
1✔
50
   */
1✔
51
  constructor(uw, source, user) {
1✔
52
    this.uw = uw;
57✔
53
    this.source = source;
57✔
54
    this.user = user;
57✔
55
  }
57✔
56
}
1✔
57

1✔
58
/**
1✔
59
 * Wrapper around playlist functions for use with import plugins. Intended to be
1✔
60
 * temporary until more data manipulation stuff is moved into core from api-v1.
1✔
61
 *
1✔
62
 * This is legacy, media sources should use the methods provided by the
1✔
63
 * `playlists` plugin instead.
1✔
64
 */
1✔
65
class ImportContext extends SourceContext {
1✔
66
  /**
1✔
67
   * Create a playlist for the current user.
1✔
68
   *
1✔
69
   * @param {string} name Playlist name.
1✔
70
   * @param {Omit<PlaylistItemDesc, 'sourceType'>[]} itemOrItems Playlist items.
1✔
71
   * @returns {Promise<Playlist>} Playlist model.
1✔
72
   */
1✔
73
  async createPlaylist(name, itemOrItems) {
1✔
NEW
74
    const { playlist } = await this.uw.playlists.createPlaylist(this.user, { name });
×
75

×
76
    const rawItems = Array.isArray(itemOrItems) ? itemOrItems : [itemOrItems];
×
77
    const items = this.source.addSourceType(rawItems);
×
78

×
79
    if (items.length > 0) {
×
80
      await this.uw.playlists.addPlaylistItems(playlist, items);
×
81
    }
×
82

×
83
    return playlist;
×
84
  }
×
85
}
1✔
86

1✔
87
/**
1✔
88
 * Wrapper around source plugins with some more convenient aliases.
1✔
89
 */
1✔
90
class Source {
1✔
91
  /**
1✔
92
   * @param {import('./Uwave.js').default} uw
1✔
93
   * @param {string} sourceType
1✔
94
   * @param {SourcePlugin} sourcePlugin
1✔
95
   */
1✔
96
  constructor(uw, sourceType, sourcePlugin) {
1✔
97
    this.uw = uw;
53✔
98
    this.type = sourceType;
53✔
99
    this.plugin = sourcePlugin;
53✔
100

53✔
101
    this.addSourceType = this.addSourceType.bind(this);
53✔
102
  }
53✔
103

1✔
104
  get apiVersion() {
1✔
105
    return this.plugin.api ?? 1;
2✔
106
  }
2✔
107

1✔
108
  /**
1✔
109
   * Add a default sourceType property to a list of media items.
1✔
110
   *
1✔
111
   * Media items can provide their own sourceType, too, so media sources can
1✔
112
   * aggregate items from different source types.
1✔
113
   *
1✔
114
   * @template T
1✔
115
   * @param {T[]} items
1✔
116
   * @returns {(T & { sourceType: string })[]}
1✔
117
   */
1✔
118
  addSourceType(items) {
1✔
119
    return items.map((item) => ({
60✔
120
      sourceType: this.type,
2,679✔
121
      ...item,
2,679✔
122
    }));
60✔
123
  }
60✔
124

1✔
125
  /**
1✔
126
   * Find a single media item by ID.
1✔
127
   *
1✔
128
   * @param {User} user
1✔
129
   * @param {string} id
1✔
130
   * @returns {Promise<SourceMedia?>}
1✔
131
   */
1✔
132
  getOne(user, id) {
1✔
133
    return this.get(user, [id])
2✔
134
      .then((items) => items[0]);
2✔
135
  }
2✔
136

1✔
137
  /**
1✔
138
   * Find several media items by ID.
1✔
139
   *
1✔
140
   * @param {User} user
1✔
141
   * @param {string[]} ids
1✔
142
   * @returns {Promise<SourceMedia[]>}
1✔
143
   */
1✔
144
  async get(user, ids) {
1✔
145
    let items;
58✔
146
    if (this.plugin.api === 2) {
58✔
147
      const context = new SourceContext(this.uw, this, user);
56✔
148
      items = await this.plugin.get(context, ids);
56✔
149
    } else {
58✔
150
      items = await this.plugin.get(ids);
2✔
151
    }
2✔
152
    return this.addSourceType(items);
58✔
153
  }
58✔
154

1✔
155
  /**
1✔
156
   * Search this media source for items. Parameters can really be anything, but
1✔
157
   * will usually include a search string `query` and a page identifier `page`.
1✔
158
   *
1✔
159
   * @template {object} TPagination
1✔
160
   * @param {User} user
1✔
161
   * @param {string} query
1✔
162
   * @param {TPagination} [page]
1✔
163
   * @param {unknown[]} args
1✔
164
   * @returns {Promise<SourceMedia[]>}
1✔
165
   */
1✔
166
  async search(user, query, page, ...args) {
1✔
167
    let results;
2✔
168
    if (this.plugin.api === 2) {
2!
169
      const context = new SourceContext(this.uw, this, user);
×
170
      results = await this.plugin.search(context, query, page, ...args);
×
171
    } else {
2✔
172
      results = await this.plugin.search(query, page, ...args);
2✔
173
    }
2✔
174
    return this.addSourceType(results);
2✔
175
  }
2✔
176

1✔
177
  /**
1✔
178
   * Playback hook. Media sources can use this to pass the necessary data for
1✔
179
   * media playback to clients, for example a temporary signed URL.
1✔
180
   *
1✔
181
   * @param {User} user
1✔
182
   * @param {PlaylistItemDesc} entry
1✔
183
   */
1✔
184
  play(user, entry) {
1✔
185
    if (this.plugin.api === 2 && this.plugin.play != null) {
7✔
186
      const context = new SourceContext(this.uw, this, user);
1✔
187
      return this.plugin.play(context, entry);
1✔
188
    }
1✔
189
    return undefined;
6✔
190
  }
7✔
191

1✔
192
  /**
1✔
193
   * Import *something* from this media source. Because media sources can
1✔
194
   * provide wildly different imports, üWave trusts clients to know what they're
1✔
195
   * doing.
1✔
196
   *
1✔
197
   * @param {User} user
1✔
198
   * @param {unknown[]} args
1✔
199
   */
1✔
200
  'import'(user, ...args) {
1✔
201
    const importContext = new ImportContext(this.uw, this, user);
×
202
    if (this.plugin.import != null) {
×
203
      return this.plugin.import(importContext, ...args);
×
204
    }
×
205
    throw new SourceNoImportError({ name: this.type });
×
206
  }
×
207
}
1✔
208

1✔
209
export {
1✔
210
  SourceContext,
1✔
211
  ImportContext,
1✔
212
  Source,
1✔
213
};
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc