@christianriedl/media 1.0.304 → 1.0.306

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/iMedia.d.ts CHANGED
@@ -367,6 +367,7 @@ export interface IPlaylistService {
367
367
  soundbarVolume: number;
368
368
  searchFeeds(query: Dictionary<any>, limit?: number): Promise<IRssFeed[]>;
369
369
  getFeed(rssUrl: string): Promise<IRssFeed | null>;
370
+ getCategories(owner: string): Promise<string[]>;
370
371
  play(url: string): Promise<boolean>;
371
372
  playList(urls: string[], clear: boolean, index?: number): Promise<boolean>;
372
373
  playCommand(cmd: EPlaylistCommand): Promise<boolean>;
@@ -9,9 +9,11 @@ export declare class PlaylistService implements IPlaylistService {
9
9
  soundbarVolume: number;
10
10
  setSoundbar: (name: string, value: string) => Promise<boolean>;
11
11
  getSoundbarValue: (name: string) => any;
12
+ cachedCategories: Dictionary<string[]>;
12
13
  constructor(rest: IRest, log: ILogger, getSoundbarValue: (name: string) => any, setSoundbar: (name: string, value: string) => Promise<boolean>);
13
14
  searchFeeds(query: Dictionary<any>, limit?: number): Promise<IRssFeed[]>;
14
15
  getFeed(rssUrl: string): Promise<IRssFeed | null>;
16
+ getCategories(owner: string): Promise<string[]>;
15
17
  play(url: string): Promise<boolean>;
16
18
  playList(urls: string[], clear: boolean, index?: number): Promise<boolean>;
17
19
  playCommand(cmd: EPlaylistCommand): Promise<boolean>;
@@ -8,6 +8,7 @@ export class PlaylistService {
8
8
  soundbarVolume;
9
9
  setSoundbar;
10
10
  getSoundbarValue;
11
+ cachedCategories;
11
12
  constructor(rest, log, getSoundbarValue, setSoundbar) {
12
13
  this.log = log;
13
14
  this.rest = rest;
@@ -15,6 +16,8 @@ export class PlaylistService {
15
16
  this.getSoundbarValue = getSoundbarValue;
16
17
  this.setSoundbar = setSoundbar;
17
18
  this.soundbarOn = false;
19
+ this.cachedCategories = {};
20
+ ;
18
21
  }
19
22
  async searchFeeds(query, limit) {
20
23
  query['limit'] = limit ?? 50;
@@ -25,6 +28,20 @@ export class PlaylistService {
25
28
  const res = await this.rest.getData('apiplaylist/feed', { rssUrl: rssUrl });
26
29
  return res.ok ? res.result : null;
27
30
  }
31
+ async getCategories(owner) {
32
+ let categories = this.cachedCategories[owner];
33
+ if (!categories) {
34
+ const res = await this.rest.getData('apiplaylist/feed/categories', { owner: owner });
35
+ if (!res.ok || !res.result) {
36
+ return [''];
37
+ }
38
+ categories = [''];
39
+ categories.push(...res.result);
40
+ this.cachedCategories[owner] = categories;
41
+ ;
42
+ }
43
+ return categories;
44
+ }
28
45
  /*
29
46
  async getTitles(): Promise<string[]> {
30
47
  const res = await this.rest.getData<string[]>('apiplaylist/title');
@@ -65,6 +82,8 @@ export class PlaylistService {
65
82
  return res.ok ? res.result : false;
66
83
  }
67
84
  async playCommand(cmd) {
85
+ if (cmd == EPlaylistCommand.Stop && this.getSoundbarValue('On') === false)
86
+ return true;
68
87
  this.soundbarOn = await this.ensureSoundbarOn();
69
88
  if (!this.soundbarOn)
70
89
  return false;
@@ -1 +1 @@
1
- {"version":3,"file":"playlistService.js","sourceRoot":"","sources":["../src/playlistService.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"playlistService.js","sourceRoot":"","sources":["../src/playlistService.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@christianriedl/media",
3
- "version": "1.0.304",
3
+ "version": "1.0.306",
4
4
  "description": "RIC media interfaces",
5
5
 
6
6
  "main": "dist/index.js",
@@ -6,6 +6,7 @@
6
6
  import { getPlaylistSymbol, IPlaylistService, IPlaylistState, IRssFeed, IRssItem, EPlaylistCommand, EFeedType } from '@christianriedl/media';
7
7
 
8
8
  const appState = inject(appStateSymbol)!;
9
+ const appConfig = inject(appConfigSymbol)!;
9
10
  const getPlaylistService = inject(getPlaylistSymbol)!;
10
11
  const playlistService = getPlaylistService() ;
11
12
  const heightStyle = computed<StyleValue>(() => { return { height: appState.bodyHeight.value + 'px', overflowY: 'auto' } });
@@ -22,6 +23,7 @@
22
23
  const listHeight = ref(0);
23
24
  const playerNames = ['Local', 'Soundbar'];
24
25
  const currentPlayer = ref<string>(playerNames[0]);
26
+ const orf = ref(true);
25
27
  const playLocal = computed(() => currentPlayer.value == 'Local');
26
28
  const playIndex = ref(-1);
27
29
  const itemIndex = ref(0);
@@ -43,21 +45,25 @@
43
45
  });
44
46
 
45
47
  const feedTypes = Helper.getEnumOptions(EFeedType);
46
- const feedType = ref<EFeedType>(0);
48
+ const feedType = ref<EFeedType>(EFeedType.Podcast);
47
49
  const countries = ["", "AT", "DE", "US"];
48
50
  const country = ref("");
49
51
  const query = ref("");
52
+ const categories = ref<string[]>([]);
53
+ const category = ref("");
54
+
50
55
  const limit = ref(50);
51
56
 
52
57
  onMounted(async () => {
58
+ statusTimer = 0;
53
59
  playlistService.log.info('OnlinePodcastsPage created');
54
60
  await start();
55
61
  });
56
62
  onUnmounted(() => {
57
63
  if (statusTimer > 0) {
58
64
  window.clearTimeout(statusTimer);
59
- statusTimer = 0;
60
65
  }
66
+ statusTimer = -1;
61
67
  });
62
68
  async function start() {
63
69
  if (route.query?.rssUrl) {
@@ -70,6 +76,9 @@
70
76
  }
71
77
  }
72
78
  else {
79
+ if (categories.value.length == 0) {
80
+ categories.value = await playlistService.getCategories('ORF');
81
+ }
73
82
  search.value = true;
74
83
  }
75
84
  computeListHeight();
@@ -88,10 +97,17 @@
88
97
  }
89
98
  async function onSearch() {
90
99
  const dict: Dictionary<any> = {};
91
- if (feedType.value != EFeedType.Unknown)
92
- dict["media"] = EFeedType[feedType.value];
93
- if (country.value != "")
94
- dict["country"] = country.value;;
100
+ if (orf.value) {
101
+ dict['owner'] = 'ORF';
102
+ if (category.value)
103
+ dict['category'] = category.value;
104
+ }
105
+ else {
106
+ if (feedType.value != EFeedType.Unknown)
107
+ dict["media"] = EFeedType[feedType.value];
108
+ if (country.value != "")
109
+ dict["country"] = country.value;;
110
+ }
95
111
  dict["query"] = query.value;
96
112
  const fds = await playlistService.searchFeeds(dict, limit.value);
97
113
  feeds.splice (0, feeds.length, ...fds);
@@ -124,6 +140,16 @@
124
140
  play();
125
141
  computeListHeight();
126
142
  }
143
+
144
+ function download(item: IRssItem) {
145
+ const anchor = document.createElement("a");
146
+ anchor.href = item.link;
147
+ anchor.download = item.title + ".mp3";
148
+ document.body.appendChild(anchor);
149
+ anchor.click();
150
+ document.body.removeChild(anchor);
151
+ }
152
+
127
153
  function listBack() {
128
154
  router.back();
129
155
  }
@@ -136,7 +162,8 @@
136
162
  volumeCount--;
137
163
  else
138
164
  volume.value = playlistService.soundbarVolume;
139
- statusTimer = window.setTimeout(async () => getStatus(), 1000);
165
+ if (statusTimer >= 0)
166
+ statusTimer = window.setTimeout(async () => getStatus(), 1000);
140
167
  }
141
168
 
142
169
  function toSubTitle(f: IRssFeed): string {
@@ -168,6 +195,15 @@
168
195
  }
169
196
  return r;
170
197
  }
198
+
199
+ function firstGenre(s?: string) {
200
+ if (!s)
201
+ return "";
202
+ const idx = s.indexOf('|');
203
+ if (idx > 0)
204
+ return s.substring(0, idx);
205
+ return s;
206
+ }
171
207
  function onEnded() {
172
208
  }
173
209
  function onTimeUpdate() {
@@ -222,8 +258,20 @@
222
258
  selectedItem.value = items[playIndex.value + 1];
223
259
  play();
224
260
  }
261
+ async function stopAll() {
262
+ if (audio.value) {
263
+ audio.value!.pause();
264
+ audio.value!.currentTime = 0;
265
+ }
266
+ await playlistService.playCommand(EPlaylistCommand.Stop);
267
+ }
225
268
  async function stop() {
226
- await playlistService.playCommand(EPlaylistCommand.Stop);
269
+ if (playLocal.value) {
270
+ audio.value!.pause();
271
+ audio.value!.currentTime = 0;
272
+ }
273
+ else
274
+ await playlistService.playCommand(EPlaylistCommand.Stop);
227
275
  }
228
276
  async function changeVolume() {
229
277
  if (volumeTimer)
@@ -243,74 +291,91 @@
243
291
  <audio ref="audio" preload="none" @ended="onEnded" @timeupdate="onTimeUpdate">
244
292
  </audio>
245
293
  <v-container v-if="search" fluid>
246
- <v-row class="align-center">
247
- <v-col cols="1"></v-col>
248
- <v-col cols="2">Type</v-col>
249
- <v-col cols="1">Country</v-col>
250
- <v-col cols="6">Query</v-col>
251
- <v-col cols="1">Limit</v-col>
294
+ <v-row class="align-center" density="compact">
295
+ <v-col cols="1">Orf</v-col>
296
+ <v-col v-if="orf" cols="3">Category</v-col>
297
+ <template v-else>
298
+ <v-col cols="2">Type</v-col>
299
+ <v-col cols="1">Country</v-col>
300
+ </template>
301
+ <v-col cols="4">Query</v-col>
302
+ <v-col cols="2">Limit</v-col>
252
303
  </v-row>
253
- <v-row class="align-center">
304
+ <v-row class="align-center" density="compact">
254
305
  <v-col cols="1">
306
+ <v-checkbox v-model="orf" hide-details density="compact"></v-checkbox>
255
307
  </v-col>
256
- <v-col cols="2">
257
- <v-select density="compact" v-model="feedType"
258
- :items="feedTypes"
308
+ <v-col v-if="orf" cols="3">
309
+ <v-select density="compact" v-model="category"
310
+ :items="categories"
259
311
  persistent-hint hide-details single-line>
260
312
  </v-select>
261
313
  </v-col>
262
- <v-col cols="1">
263
- <v-combobox density="compact" v-model="country"
264
- :items="countries"
265
- persistent-hint hide-details single-line>
266
- </v-combobox>
267
- </v-col>
268
- <v-col cols="6">
314
+ <template v-else>
315
+ <v-col cols="2">
316
+ <v-select density="compact" v-model="feedType"
317
+ :items="feedTypes"
318
+ persistent-hint hide-details single-line>
319
+ </v-select>
320
+ </v-col>
321
+ <v-col cols="1">
322
+ <v-combobox density="compact" v-model="country"
323
+ :items="countries"
324
+ persistent-hint hide-details single-line>
325
+ </v-combobox>
326
+ </v-col>
327
+ </template>
328
+ <v-col cols="4">
269
329
  <v-text-field v-model="query" hide-details density="compact"></v-text-field>
270
330
  </v-col>
271
- <v-col cols="1">
331
+ <v-col cols="2">
272
332
  <v-text-field v-model="limit" hide-details type="number" density="compact"></v-text-field>
273
333
  </v-col>
274
334
  <v-col cols="1">
275
- <v-btn icon="$search" class="bg-media_head" variant="tonal" @click="onSearch">
276
- </v-btn>
335
+ <v-btn icon="$search" class="bg-media_head" variant="tonal" density="default" @click="onSearch"></v-btn>
336
+ </v-col>
337
+ <v-col cols="1">
338
+ <v-btn icon="$stop" class="bg-media_head" variant="tonal" density="default" @click="stopAll"></v-btn>
277
339
  </v-col>
278
340
  </v-row>
279
341
  </v-container>
280
342
  <v-container v-else fluid>
281
- <v-row class="align-center">
343
+ <v-row class="align-center" density="compact">
282
344
  <v-col cols="1">
283
- <v-btn icon="$back" class="bg-media_head" variant="tonal" @click="listBack">
284
- </v-btn>
345
+ <v-btn icon="$back" class="bg-media_head" variant="tonal" density="compact" @click="listBack"></v-btn>
285
346
  </v-col>
286
347
  <v-col cols="1">
287
348
  <v-img :src="selectedFeed!.imageUrl"></v-img>
288
349
  </v-col>
289
350
  <v-col cols="1">{{selectedFeed!.country}}</v-col>
290
- <v-col cols="2">{{selectedFeed!.genre}}</v-col>
351
+ <v-col cols="2">{{firstGenre(selectedFeed!.genre)}}
352
+ <v-tooltip v-if="!isMobile && selectedFeed!.genre && selectedFeed!.genre.indexOf('|') > 0" activator="parent" location="top">
353
+ {{selectedFeed!.genre.replaceAll('|', ', ')}}
354
+ </v-tooltip>
355
+ </v-col>
291
356
  <v-col cols="2">{{selectedFeed!.owner}}</v-col>
292
- <v-col cols="4">{{selectedFeed!.title}}</v-col>
293
- <v-col cols="1">
357
+ <v-col :cols="(appConfig.isLocal && isMobile) ? 3 : 4">{{selectedFeed!.title}}</v-col>
358
+ <v-col v-if="appConfig.isLocal" :cols="isMobile ? 2 : 1">
294
359
  <v-select v-model="currentPlayer" density="compact" :items="playerNames" persistent-hint
295
360
  @update:modelValue="onPlayerChange" hide-details single-line>
296
361
  </v-select>
297
362
  </v-col>
298
363
  </v-row>
299
- <v-row v-if="playingItem" class="align-center">
300
- <v-col cols="4">
301
- <v-btn :icon="paused ? '$play' : '$pause'" class="bg-media_head" variant="tonal" @click="playpause"></v-btn>
302
- <v-btn v-if="!playLocal" icon="$stop" class="bg-media_head" variant="tonal" @click="stop"></v-btn>
303
- <v-btn icon="$prev" class="bg-media_head" variant="tonal" @click="previous"></v-btn>
304
- <v-btn icon="$next" class="bg-media_head" variant="tonal" @click="next"></v-btn>
364
+ <v-row v-if="playingItem" class="align-center" density="compact">
365
+ <v-col :cols="isMobile ? 5 : 3">
366
+ <v-btn :icon="paused ? '$play' : '$pause'" class="bg-media_head" variant="tonal" density="compact" @click="playpause"></v-btn>
367
+ <v-btn icon="$stop" class="bg-media_head" variant="tonal" density="compact" @click="stop"></v-btn>
368
+ <v-btn icon="$prev" class="bg-media_head" variant="tonal" density="compact" @click="previous"></v-btn>
369
+ <v-btn icon="$next" class="bg-media_head" variant="tonal" density="compact" @click="next"></v-btn>
305
370
  </v-col>
306
- <v-col cols ="8">
371
+ <v-col :cols ="isMobile? 7 : 9">
307
372
  <v-slider v-if="!playLocal" style="width:30%" color="red" track-color="blue" track-fill-color="red" v-model="volume" hide-details density="compact"
308
373
  min="0" max="100" step="5" @update:modelValue="changeVolume">
309
374
  </v-slider>
310
375
  <v-div v-else>{{playingItem.title}}</v-div>
311
376
  </v-col>
312
377
  </v-row>
313
- <v-row v-if="playingItem">
378
+ <v-row v-if="playingItem" density="compact">
314
379
  <v-col cols="12">
315
380
  <v-progress-linear v-model="positionLength" color="blue" height="25"><strong>{{positionText}}</strong></v-progress-linear>
316
381
  </v-col>
@@ -319,69 +384,35 @@
319
384
  </v-card>
320
385
  <v-card :height="listHeight" density="compact" class="overflow-y-auto bg-media">
321
386
  <v-container v-if="search" fluid>
322
- <v-row v-for="(feed,index) in feeds" :key="feed.rssUrl" @click="listFeed(feed)">
387
+ <v-row v-for="(feed,index) in feeds" :key="feed.rssUrl" density="compact" @click="listFeed(feed)">
323
388
  <v-col cols="1">{{feed.country}}</v-col>
324
- <v-col cols="2">{{feed.genre}}</v-col>
389
+ <v-col cols="2">{{firstGenre(feed.genre)}}
390
+ <v-tooltip v-if="!isMobile && feed.genre && feed.genre.indexOf('|') > 0" activator="parent" location="top">
391
+ {{feed.genre.replaceAll('|', ', ')}}
392
+ </v-tooltip>
393
+ </v-col>
325
394
  <v-col cols="2">{{feed.owner}}</v-col>
326
- <v-col cols="5">{{feed.title}}</v-col>
395
+ <v-col cols="5">{{feed.title}}
396
+ <v-tooltip v-if="!isMobile && feed.description" activator="parent" location="top">
397
+ <div v-html="feed.description"></div>
398
+ </v-tooltip>
399
+ </v-col>
327
400
  </v-row>
328
401
  </v-container>
329
- <v-container v-else>
330
- <v-row v-for="(item,index) in items" :key="item.title" :class="{ active: index == playIndex }" @click="listItem(item)">
331
- <v-col cols="6">{{item.title}}</v-col>
332
- <v-col cols="6">{{toTime(item.duration)}}</v-col>
402
+ <v-container v-else fluid>
403
+ <v-row v-for="(item,index) in items" :key="item.title" density="compact" :class="{ active: index == playIndex }" @click="listItem(item)">
404
+ <v-col cols="8">{{item.title}}
405
+ <v-tooltip v-if="!isMobile && item.description" activator="parent" location="top">
406
+ <div v-html="item.description"></div>
407
+ </v-tooltip>
408
+ </v-col>
409
+ <v-col cols="2">{{toTime(item.duration)}}</v-col>
410
+ <v-col cols="2">
411
+ <v-btn icon="$download" class="bg-media" density="compact" variant="flat" @click.stop="download(item)"></v-btn>
412
+ </v-col>
333
413
  </v-row>
334
414
  </v-container>
335
415
  </v-card>
336
-
337
- <!--
338
- <v-card class="bg-media_head">
339
- <audio ref="audio" preload="none" @ended="onEnded" @timeupdate="onTimeUpdate">
340
- </audio>
341
- <v-list-item lines="three">
342
- <v-list-item-content>
343
- <v-list-item-title text-h6>{{selected?.title}}</v-list-item-title>
344
- <v-list-item-subtitle>{{toSubTitle(selected!)}}</v-list-item-subtitle>
345
- </v-list-item-content>
346
- <template v-slot:append>
347
- <v-select v-model="currentPlayer" density="compact"
348
- :items="playerNames"
349
- persistent-hint
350
- @update:modelValue="onPlayerChange"
351
- hide-details single-line>
352
- </v-select>
353
- </template>
354
- </v-list-item>
355
- <v-card-actions>
356
- <v-btn v-if="backVisible" @click="listBack">
357
- <v-icon icon="$back" />
358
- </v-btn>
359
- <v-btn size="small" v-if="playingFeed" @click="playpause">
360
- <v-icon v-if="paused" icon="$play" />
361
- <v-icon v-else icon="$pause" />
362
- </v-btn>
363
- <v-btn size="small" v-if="playingFeed" @click="previous">
364
- <v-icon icon="$prev" />
365
- </v-btn>
366
- <v-btn size="small" v-if="playingFeed" @click="next">
367
- <v-icon icon="$next" />
368
- </v-btn>
369
- <v-slider v-if="!playLocal && playingFeed" style="width:30%" color="red" track-color="blue" track-fill-color="red" v-model="volume" hide-details density="compact"
370
- min="0" max="100" step="5" @update:modelValue="changeVolume">
371
- </v-slider>
372
- </v-card-actions>
373
- <v-progress-linear v-if="playingFeed" v-model="positionLength" color="blue" height="25"><strong>{{positionText}}</strong></v-progress-linear>
374
- </v-card>
375
- <v-card :height="listHeight" density="compact" class="overflow-y-auto bg-media">
376
- <v-list lines="two" class="bg-media">
377
- <v-list-item-group v-model="itemIndex">
378
- <v-list-item v-for="(feed,index) in feeds" :key="feed.title" density="compact" :title="feed.title" :subtitle="toSubTitle(feed)"
379
- active-color="blue" :active="index == playIndex" @click="listItem(feed)">
380
- </v-list-item>
381
- </v-list-item-group>
382
- </v-list>
383
- </v-card>
384
- -->
385
416
  </template>
386
417
 
387
418
  <style scoped>