@christianriedl/media 1.0.301 → 1.0.303

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
@@ -317,15 +317,33 @@ export interface IMediaService {
317
317
  getCloudUrl(folder: string, name: string): Promise<string>;
318
318
  getStatus(): Promise<Dictionary<boolean | number | string> | null>;
319
319
  }
320
- export interface IRssFeed {
320
+ export interface IRssItem {
321
321
  title: string;
322
- description: string;
322
+ description?: string;
323
323
  link: string;
324
- copyright?: string;
325
- items?: IRssFeed[];
326
324
  mediaType?: string;
327
325
  duration?: number;
328
326
  }
327
+ export declare enum EFeedType {
328
+ Unknown = 0,
329
+ Movie = 1,
330
+ Podcast = 2,
331
+ Music = 3,
332
+ MusicVideo = 4,
333
+ Audiobook = 5,
334
+ ShortFilm = 6,
335
+ TvShow = 7,
336
+ Ebook = 8
337
+ }
338
+ export interface IRssFeed extends IRssItem {
339
+ rssUrl: string;
340
+ genre?: string;
341
+ country?: string;
342
+ copyright?: string;
343
+ owner?: string;
344
+ imageUrl?: string;
345
+ items?: IRssItem[];
346
+ }
329
347
  export declare const getPlaylistSymbol: InjectionKey<() => IPlaylistService>;
330
348
  export declare enum EPlaylistCommand {
331
349
  None = 0,
@@ -344,12 +362,10 @@ export interface IPlaylistState {
344
362
  position: string;
345
363
  }
346
364
  export interface IPlaylistService {
365
+ log: ILogger;
347
366
  soundbarOn: boolean;
348
- getTitles(): Promise<string[]>;
349
- getFeed(title: string): Promise<IRssFeed | null>;
350
- getFeeds(): Promise<IRssFeed[]>;
351
- addFeed(url: string, save: boolean): Promise<IRssFeed | null>;
352
- deleteFeed(title: string): Promise<boolean>;
367
+ searchFeeds(query: Dictionary<any>, limit?: number): Promise<IRssFeed[]>;
368
+ getFeed(rssUrl: string): Promise<IRssFeed | null>;
353
369
  play(url: string): Promise<boolean>;
354
370
  playList(urls: string[], clear: boolean, index?: number): Promise<boolean>;
355
371
  playCommand(cmd: EPlaylistCommand): Promise<boolean>;
package/dist/iMedia.js CHANGED
@@ -121,6 +121,18 @@ export var EPictureFlags;
121
121
  EPictureFlags[EPictureFlags["Mask"] = 240] = "Mask";
122
122
  })(EPictureFlags || (EPictureFlags = {}));
123
123
  export const getMediaSymbol = Symbol("get-media");
124
+ export var EFeedType;
125
+ (function (EFeedType) {
126
+ EFeedType[EFeedType["Unknown"] = 0] = "Unknown";
127
+ EFeedType[EFeedType["Movie"] = 1] = "Movie";
128
+ EFeedType[EFeedType["Podcast"] = 2] = "Podcast";
129
+ EFeedType[EFeedType["Music"] = 3] = "Music";
130
+ EFeedType[EFeedType["MusicVideo"] = 4] = "MusicVideo";
131
+ EFeedType[EFeedType["Audiobook"] = 5] = "Audiobook";
132
+ EFeedType[EFeedType["ShortFilm"] = 6] = "ShortFilm";
133
+ EFeedType[EFeedType["TvShow"] = 7] = "TvShow";
134
+ EFeedType[EFeedType["Ebook"] = 8] = "Ebook";
135
+ })(EFeedType || (EFeedType = {}));
124
136
  export const getPlaylistSymbol = Symbol("get-playlist");
125
137
  export var EPlaylistCommand;
126
138
  (function (EPlaylistCommand) {
@@ -1 +1 @@
1
- {"version":3,"file":"iMedia.js","sourceRoot":"","sources":["../src/iMedia.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"iMedia.js","sourceRoot":"","sources":["../src/iMedia.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  import { IRest } from '@christianriedl/rest';
2
- import { ILogger } from '@christianriedl/utils';
2
+ import { ILogger, Dictionary } from '@christianriedl/utils';
3
3
  import { IRssFeed, EPlaylistCommand, IPlaylistState, IPlaylistService } from "./iMedia";
4
4
  export declare class PlaylistService implements IPlaylistService {
5
5
  rest: IRest;
@@ -7,13 +7,10 @@ export declare class PlaylistService implements IPlaylistService {
7
7
  log: ILogger;
8
8
  soundbarOn: boolean;
9
9
  setSoundbar: (name: string, value: string) => Promise<boolean>;
10
- getSoundbarValue: (name: string) => string;
11
- constructor(rest: IRest, log: ILogger, getSoundbarValue: (name: string) => string, setSoundbar: (name: string, value: string) => Promise<boolean>);
12
- getTitles(): Promise<string[]>;
13
- getFeed(title: string): Promise<IRssFeed | null>;
14
- getFeeds(): Promise<IRssFeed[]>;
15
- addFeed(url: string, save: boolean): Promise<IRssFeed | null>;
16
- deleteFeed(title: string): Promise<boolean>;
10
+ getSoundbarValue: (name: string) => any;
11
+ constructor(rest: IRest, log: ILogger, getSoundbarValue: (name: string) => any, setSoundbar: (name: string, value: string) => Promise<boolean>);
12
+ searchFeeds(query: Dictionary<any>, limit?: number): Promise<IRssFeed[]>;
13
+ getFeed(rssUrl: string): Promise<IRssFeed | null>;
17
14
  play(url: string): Promise<boolean>;
18
15
  playList(urls: string[], clear: boolean, index?: number): Promise<boolean>;
19
16
  playCommand(cmd: EPlaylistCommand): Promise<boolean>;
@@ -15,26 +15,37 @@ export class PlaylistService {
15
15
  this.setSoundbar = setSoundbar;
16
16
  this.soundbarOn = false;
17
17
  }
18
- async getTitles() {
19
- const res = await this.rest.getData('apiplaylist/title');
18
+ async searchFeeds(query, limit) {
19
+ query['limit'] = limit ?? 50;
20
+ const res = await this.rest.getData('apiplaylist/feed/search', query);
20
21
  return res.ok ? res.result : [];
21
22
  }
22
- async getFeed(title) {
23
- const res = await this.rest.getData('apiplaylist/feed', { title: title });
23
+ async getFeed(rssUrl) {
24
+ const res = await this.rest.getData('apiplaylist/feed', { rssUrl: rssUrl });
24
25
  return res.ok ? res.result : null;
25
26
  }
26
- async getFeeds() {
27
- const res = await this.rest.getData('apiplaylist/feed');
28
- return res.ok ? res.result : [];
27
+ /*
28
+ async getTitles(): Promise<string[]> {
29
+ const res = await this.rest.getData<string[]>('apiplaylist/title');
30
+ return res.ok ? res.result as string[] : [];
29
31
  }
30
- async addFeed(url, save) {
31
- const res = await this.rest.postDataEx(`apiplaylist/feed?url=${encodeURIComponent(url)}&save=${save}`);
32
- return res.ok ? res.result.result : null;
32
+ async getFeed(title: string): Promise<IRssFeed|null> {
33
+ const res = await this.rest.getData<IRssFeed>('apiplaylist/feed', { title: title });
34
+ return res.ok ? res.result as IRssFeed : null;
33
35
  }
34
- async deleteFeed(title) {
35
- const res = await this.rest.deleteData(`apiplaylist/feed?title=${encodeURIComponent(title)}`);
36
- return res.ok ? res.result : false;
36
+ async getFeeds(): Promise<IRssFeed[]> {
37
+ const res = await this.rest.getData<IRssFeed[]>('apiplaylist/feed');
38
+ return res.ok ? res.result as IRssFeed[] : [];
39
+ }
40
+ async addFeed(url: string, save: boolean): Promise<IRssFeed|null> {
41
+ const res = await this.rest.postDataEx<boolean,IRssFeed>(`apiplaylist/feed?url=${encodeURIComponent(url)}&save=${save}`);
42
+ return res.ok ? (res.result as IValueResult<IRssFeed>).result : null;
43
+ }
44
+ async deleteFeed(title: string): Promise<boolean> {
45
+ const res = await this.rest.deleteData<any, boolean>(`apiplaylist/feed?title=${encodeURIComponent(title)}`);
46
+ return res.ok ? res.result as boolean : false;
37
47
  }
48
+ */
38
49
  async play(url) {
39
50
  this.soundbarOn = await this.ensureOn();
40
51
  if (!this.soundbarOn)
@@ -67,9 +78,9 @@ export class PlaylistService {
67
78
  async ensureOn() {
68
79
  let i = 0;
69
80
  for (i = 0; i < 5; i++) {
70
- if (this.getSoundbarValue('ON') === 'ON')
81
+ if (this.getSoundbarValue('On') === true)
71
82
  break;
72
- const result = await this.setSoundbar('POWER', 'on');
83
+ const result = await this.setSoundbar('POWERON', '1');
73
84
  await Helper.delay(1000);
74
85
  }
75
86
  if (i >= 5)
@@ -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":"AACA,OAAO,EAAW,MAAM,EAAc,MAAM,uBAAuB,CAAC;AACpE,OAAO,EAAY,gBAAgB,EAAoC,MAAM,UAAU,CAAC;AAExF,MAAM,OAAO,eAAe;IACxB,IAAI,CAAQ;IACZ,QAAQ,CAAS;IACjB,GAAG,CAAU;IACb,UAAU,CAAU;IACpB,WAAW,CAAoD;IAC/D,gBAAgB,CAAwB;IACxC,YAAY,IAAW,EAAE,GAAY,EAAE,gBAAuC,EAAE,WAA8D;QAC1I,IAAI,CAAC,GAAG,GAAG,GAAG,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,UAAU,CAAC;QAChC,IAAI,CAAC,gBAAgB,GAAG,gBAAgB,CAAC;QACzC,IAAI,CAAC,WAAW,GAAG,WAAW,CAAC;QACtC,IAAI,CAAC,UAAU,GAAG,KAAK,CAAC;IACrB,CAAC;IACD,KAAK,CAAC,WAAW,CAAC,KAAsB,EAAE,KAAc;QAC3D,KAAK,CAAC,OAAO,CAAC,GAAG,KAAK,IAAI,EAAE,CAAC;QACtB,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,OAAO,CAAa,yBAAyB,EAAE,KAAK,CAAC,CAAC;QAClF,OAAO,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,MAAoB,CAAC,CAAC,CAAC,EAAE,CAAC;IAClD,CAAC;IACD,KAAK,CAAC,OAAO,CAAC,MAAc;QACxB,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,OAAO,CAAW,kBAAkB,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;QACtF,OAAO,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,MAAkB,CAAC,CAAC,CAAC,IAAI,CAAC;IAClD,CAAC;IACD;;;;;;;;;;;;;;;;;;;;;MAqBE;IACF,KAAK,CAAC,IAAI,CAAC,GAAW;QACzB,IAAI,CAAC,UAAU,GAAG,MAAM,IAAI,CAAC,QAAQ,EAAE,CAAC;QACxC,IAAI,CAAC,IAAI,CAAC,UAAU;YAChB,OAAO,KAAK,CAAC;QACV,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAM,wBAAwB,kBAAkB,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAC7F,OAAO,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,MAAiB,CAAC,CAAC,CAAC,KAAK,CAAC;IAClD,CAAC;IACD,KAAK,CAAC,QAAQ,CAAC,IAAc,EAAE,KAAc,EAAE,KAAc;QAChE,IAAI,CAAC,UAAU,GAAG,MAAM,IAAI,CAAC,QAAQ,EAAE,CAAC;QACxC,IAAI,CAAC,IAAI,CAAC,UAAU;YAChB,OAAO,KAAK,CAAC;QACjB,IAAI,GAAG,GAAG,0BAA0B,KAAK,EAAE,CAAC;QAC5C,IAAI,KAAK,KAAK,SAAS;YACnB,GAAG,IAAI,UAAU,KAAK,EAAE,CAAC;QACtB,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAW,GAAG,EAAE,IAAI,CAAC,CAAC;QAC1D,OAAO,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,MAAiB,CAAC,CAAC,CAAC,KAAK,CAAC;IAClD,CAAC;IACD,KAAK,CAAC,WAAW,CAAC,GAAqB;QAC1C,IAAI,CAAC,UAAU,GAAG,MAAM,IAAI,CAAC,QAAQ,EAAE,CAAC;QACxC,IAAI,CAAC,IAAI,CAAC,UAAU;YAChB,OAAO,KAAK,CAAC;QACjB,IAAI,GAAG,GAAG,+BAA+B,gBAAgB,CAAC,GAAG,CAAC,EAAE,CAAC;QAC1D,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAM,GAAG,CAAC,CAAC;QAC/C,OAAO,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,MAAiB,CAAC,CAAC,CAAC,KAAK,CAAC;IAClD,CAAC;IACD,KAAK,CAAC,QAAQ;QACV,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,OAAO,CAAiB,mBAAmB,CAAC,CAAC;QACzE,OAAO,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,MAAwB,CAAC,CAAC,CAAC,IAAI,CAAC;IACxD,CAAC;IACD,KAAK,CAAC,QAAQ;QACjB,IAAI,CAAC,GAAG,CAAC,CAAC;QACV,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;YACrB,IAAI,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,KAAK,IAAI;gBAC3C,MAAM;YACH,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC;YACtD,MAAM,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAC7B,CAAC;QACD,IAAI,CAAC,IAAI,CAAC;YACN,OAAO,KAAK,CAAC;QACjB,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;YACrB,IAAI,IAAI,CAAC,gBAAgB,CAAC,MAAM,CAAC,KAAK,SAAS;gBAClD,OAAO,IAAI,CAAC;YACT,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;YAC3D,MAAM,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAC7B,CAAC;QACD,OAAO,KAAK,CAAC;IACV,CAAC;CACJ"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@christianriedl/media",
3
- "version": "1.0.301",
3
+ "version": "1.0.303",
4
4
  "description": "RIC media interfaces",
5
5
 
6
6
  "main": "dist/index.js",
@@ -244,6 +244,9 @@
244
244
  function onlineRadio() {
245
245
  router.push({ path: 'onlineradios' });
246
246
  }
247
+ function onlinePodcast() {
248
+ router.push({ path: 'onlinepodcasts' });
249
+ }
247
250
  function download() {
248
251
  window.open(playingTrackUrl.value);
249
252
  }
@@ -319,9 +322,14 @@
319
322
  <v-btn v-if="backVisible" @click="listBack">
320
323
  <v-icon icon="$back" />
321
324
  </v-btn>
322
- <v-btn v-else @click.stop="onlineRadio">
325
+ <template v-else>
326
+ <v-btn @click.stop="onlineRadio">
323
327
  <v-icon size="x-large" icon="$radio" />
324
328
  </v-btn>
329
+ <v-btn @click.stop="onlinePodcast">
330
+ <v-icon size="x-large" icon="$podcast" />
331
+ </v-btn>
332
+ </template>
325
333
  <v-btn v-if="downloadAlbumVisible" @click="downloadAlbum">
326
334
  Album
327
335
  <v-icon icon="$download" />
@@ -0,0 +1,372 @@
1
+ <script setup lang="ts">
2
+ import { inject, ref, reactive, computed, onMounted, onUnmounted, nextTick, watch, StyleValue } from 'vue';
3
+ import type { ComponentPublicInstance } from 'vue';
4
+ import { useRouter, useRoute } from 'vue-router';
5
+ import { IAppState, EScope, EDevice, Helper, appStateSymbol, appConfigSymbol, Dictionary } from '@christianriedl/utils';
6
+ import { getPlaylistSymbol, IPlaylistService, IPlaylistState, IRssFeed, IRssItem, EPlaylistCommand, EFeedType } from '@christianriedl/media';
7
+
8
+ const appState = inject(appStateSymbol)!;
9
+ const getPlaylistService = inject(getPlaylistSymbol)!;
10
+ const playlistService = getPlaylistService() ;
11
+ const heightStyle = computed<StyleValue>(() => { return { height: appState.bodyHeight.value + 'px', overflowY: 'auto' } });
12
+ const isMobile = appState.isMobile && (appState.device != EDevice.iPad);
13
+ const router = useRouter();
14
+ const route = useRoute();
15
+ const feeds: IRssFeed[] = reactive([]);
16
+ const items: IRssItem[] = reactive([]);
17
+ const selectedItem = ref<IRssItem|null>(null);
18
+ const selectedFeed = ref<IRssFeed|null>(null);
19
+ const status = ref<IPlaylistState|null>(null);
20
+ const audio = ref<HTMLAudioElement | null>(null);
21
+ const listhead = ref<ComponentPublicInstance | null>(null);
22
+ const listHeight = ref(0);
23
+ const playerNames = ['Local', 'Soundbar'];
24
+ const backVisible = computed(() => !!route.query?.feed);
25
+ const currentPlayer = ref<string>(playerNames[0]);
26
+ const playLocal = computed(() => currentPlayer.value == 'Local');
27
+ const playIndex = ref(-1);
28
+ const itemIndex = ref(0);
29
+ const playingItem = ref<IRssItem|null>(null);
30
+ const position = ref(0);
31
+ const positionLength = ref(0);
32
+ const volume = ref(0);
33
+ const paused = ref(false);
34
+ const search = ref(true);
35
+ let statusTimer = 0;
36
+ const positionText = computed(() => {
37
+ if (playingItem.value?.duration)
38
+ return toTime(position.value) + '/' + toTime(playingItem.value.duration);
39
+ else
40
+ return toTime(position.value);
41
+ });
42
+
43
+ const feedTypes = Helper.getEnumOptions(EFeedType);
44
+ const feedType = ref<EFeedType>(0);
45
+ const countries = ["", "AT", "DE", "US"];
46
+ const country = ref("");
47
+ const query = ref("");
48
+ const limit = ref(50);
49
+
50
+ onMounted(async () => {
51
+ playlistService.log.info('OnlinePodcastsPage created');
52
+ await start();
53
+ });
54
+ onUnmounted(() => {
55
+ if (statusTimer > 0) {
56
+ clearTimeout(statusTimer);
57
+ statusTimer = 0;
58
+ }
59
+ });
60
+ async function start() {
61
+ if (route.query?.rssUrl) {
62
+ const rssUrl = decodeURIComponent(route.query.rssUrl.toString());
63
+ const f = await playlistService.getFeed(rssUrl);
64
+ if (f && f.items) {
65
+ selectedFeed.value = f;
66
+ items.splice(0, items.length, ...f.items);
67
+ search.value = false;
68
+ }
69
+ }
70
+ else {
71
+ search.value = true;
72
+ }
73
+ computeListHeight();
74
+ }
75
+ watch(appState.bodyHeight, () => computeListHeight());
76
+ watch(() => route.query, () => start());
77
+
78
+ function computeListHeight() {
79
+ nextTick(() => {
80
+ let height = appState.bodyHeight.value;
81
+ if (listhead.value) {
82
+ height = height - (listhead.value.$el as HTMLElement).clientHeight;
83
+ }
84
+ listHeight.value = height;
85
+ })
86
+ }
87
+
88
+ async function onSearch() {
89
+ const dict: Dictionary<any> = {};
90
+ if (feedType.value != EFeedType.None)
91
+ dict["media"] = EFeedType[feedType];
92
+ if (country.value != "")
93
+ dict["country"] = country.value;;
94
+ dict["query"] = query.value;
95
+ const fds = await playlistService.searchFeeds(dict, limit.value);;
96
+ feeds.splice (0, feeds.length, ...fds);
97
+ }
98
+ async function play() {
99
+ if (playLocal.value) {
100
+ audio.value!.src = selectedItem.value!.link;
101
+ audio.value!.load();
102
+ audio.value!.play().then(() => {
103
+ playlistService.log.trace(`play local ${selectedItem.value!.title}`)
104
+ });
105
+ playingItem.value = selectedItem.value;
106
+ }
107
+ else {
108
+ const rc = await playlistService.play(selectedItem.value!.link);
109
+ if (!rc)
110
+ return;
111
+ playingItem.value = selectedItem.value;
112
+ await getStatus();
113
+ playlistService.log.trace(`play remote ${selectedItem.value!.title}`)
114
+ }
115
+ itemIndex.value = playIndex.value = feeds.findIndex((f) => f.link == selectedItem.value!.link);
116
+ }
117
+ function listFeed(feed: IRssFeed) {{
118
+ router.push ({path: 'onlinePodcasts', query: { rssUrl: feed.rssUrl }});
119
+ }
120
+
121
+ function listItem(item: IRssItem) {
122
+ selectedItem.value = item;
123
+ play();
124
+ computeListHeight();
125
+ }
126
+ function listBack() {
127
+ router.back();
128
+ }
129
+ async function getStatus() {
130
+ const state = await playlistService.getState();
131
+ status.value = state;
132
+ if (state)
133
+ setPosition(Number(state.position));
134
+ statusTimer = window.setTimeout(async () => getStatus(), 1000);
135
+ }
136
+
137
+ function toSubTitle(f: IRssFeed): string {
138
+ if (!f || !f.duration)
139
+ return "";
140
+ return toTime(f.duration);
141
+ }
142
+
143
+ function toTime(s: number): string {
144
+ if (!s)
145
+ return "";
146
+ let r = "";
147
+ s = Math.floor(s);
148
+ if (s >= 3600) {
149
+ r += Math.floor(s / 3600).toString() + ":"; s = s % 3600;
150
+ }
151
+ if (!r || s >= 600) {
152
+ r += Math.floor(s / 60).toString() + ":"; s = s % 60;
153
+ }
154
+ else {
155
+ r += "0"; r += Math.floor(s / 60).toString() + ":"; s = s % 60;
156
+ }
157
+ if (s >= 10) {
158
+ r += s.toString(); s = -1;
159
+ }
160
+ else if (s >= 0) {
161
+ r += "0"; r += s.toString();
162
+ s = -1;
163
+ }
164
+ return r;
165
+ }
166
+ function onEnded() {
167
+ }
168
+ function onTimeUpdate() {
169
+ setPosition(audio.value!.currentTime);
170
+ }
171
+ function setPosition(value: number) {
172
+ position.value = value;
173
+ if (playingItem.value?.duration)
174
+ positionLength.value = position.value * 100 / playingItem.value!.duration;
175
+ else
176
+ positionLength.value = 0;
177
+ }
178
+ function onPlayerChange() {
179
+ if (statusTimer) {
180
+ clearTimeout(statusTimer);
181
+ statusTimer = 0;
182
+ }
183
+ if (!playLocal.value) {
184
+ statusTimer = window.setTimeout(() => getStatus(), 1000);
185
+ }
186
+ }
187
+ function playpause() {
188
+ if (playLocal.value) {
189
+ if (audio.value!.paused) {
190
+ paused.value = false;
191
+ audio.value!.play()
192
+ .then(() => { console.log("play ended") });
193
+ }
194
+ else {
195
+ paused.value = true;
196
+ audio.value!.pause();
197
+ }
198
+ }
199
+ else {
200
+ if (paused.value) {
201
+ playlistService.playCommand(EPlaylistCommand.Resume);
202
+ }
203
+ else {
204
+ playlistService.playCommand(EPlaylistCommand.Pause);
205
+ }
206
+ }
207
+ }
208
+ function previous() {
209
+ if (playIndex.value <= 0)
210
+ return;
211
+ selectedItem.value = feeds[playIndex.value - 1];
212
+ play();
213
+ }
214
+ function next() {
215
+ if (playIndex.value >= feeds.length - 1)
216
+ return;
217
+ selectedItem.value = feeds[playIndex.value + 1];
218
+ play();
219
+ }
220
+ function changeVolume() {
221
+ }
222
+
223
+ </script>
224
+
225
+ <template>
226
+ <v-card ref="listhead" class="bg-media_head">
227
+ <audio ref="audio" preload="none" @ended="onEnded" @timeupdate="onTimeUpdate">
228
+ </audio>
229
+ <v-container v-if="search" fluid>
230
+ <v-row class="align-center">
231
+ <v-col cols="1"></v-col>
232
+ <v-col cols="2">Type</v-col>
233
+ <v-col cols="1">Country</v-col>
234
+ <v-col cols="6">Query</v-col>
235
+ <v-col cols="1">Limit</v-col>
236
+ </v-row>
237
+ <v-row class="align-center">
238
+ <v-col cols="1">
239
+ <v-btn v-if="backVisible" icon="$back" class="bg-media_head" variant="tonal" @click="listBack">
240
+ </v-btn>
241
+ </v-col>
242
+ <v-col cols="2">
243
+ <v-select density="compact" v-model="feedType"
244
+ :items="feedTypes"
245
+ persistent-hint hide-details single-line>
246
+ </v-select>
247
+ </v-col>
248
+ <v-col cols="1">
249
+ <v-combobox density="compact" v-model="country"
250
+ :items="countries"
251
+ persistent-hint hide-details single-line>
252
+ </v-combobox>
253
+ </v-col>
254
+ <v-col cols="6">
255
+ <v-text-field v-model="query" hide-details density="compact"></v-text-field>
256
+ </v-col>
257
+ <v-col cols="1">
258
+ <v-text-field v-model="limit" hide-details type="number" density="compact"></v-text-field>
259
+ </v-col>
260
+ <v-col cols="1">
261
+ <v-btn icon="$search" class="bg-media_head" variant="tonal" @click="onSearch">
262
+ </v-btn>
263
+ </v-col>
264
+ </v-row>
265
+ </v-container>
266
+ <v-container v-else fluid>
267
+ <v-row class="align-center">
268
+ <v-col cols="1">
269
+ <v-btn v-if="backVisible" icon="$back" class="bg-media_head" variant="tonal" @click="listBack">
270
+ </v-btn>
271
+ </v-col>
272
+ <v-col cols="1">
273
+ <v-img :src="selectedFeed.imageUrl"></v-img>
274
+ </v-col>
275
+ <v-col cols="1">{{selectedFeed.country}}</v-col>
276
+ <v-col cols="2">{{selectedFeed.genre}}</v-col>
277
+ <v-col cols="2">{{selectedFeed.owner}}</v-col>
278
+ <v-col cols="5">{{selectedFeed.title}}</v-col>
279
+ <v-col cols="2">
280
+ <v-select v-model="currentPlayer" density="compact" :items="playerNames" persistent-hint
281
+ @update:modelValue="onPlayerChange" hide-details single-line>
282
+ </v-select>
283
+ </v-col>
284
+ </v-row>
285
+ <v-row v-if="playingItem" class="align-center">
286
+ <v-col cols="4">
287
+ <v-btn :icon="paused ? '$play' : '$pause'" @click="playpause">
288
+ <v-btn icon="$prev" @click="previous">
289
+ <v-btn icon="$next" @click="next">
290
+ </v-col>
291
+ <v-col cols ="8">
292
+ <v-slider v-if="!playLocal" style="width:30%" color="red" track-color="blue" track-fill-color="red" v-model="volume" hide-details density="compact"
293
+ min="0" max="100" step="5" @update:modelValue="changeVolume">
294
+ </v-slider>
295
+ </v-col>
296
+ </v-row>
297
+ <v-row v-if="playingItem">
298
+ <v-col cols="12">
299
+ <v-progress-linear v-model="positionLength" color="blue" height="25"><strong>{{positionText}}</strong></v-progress-linear>
300
+ </v-col>
301
+ </v-row>
302
+ </v-container>
303
+ </v-card>
304
+ <v-card :height="listHeight" density="compact" class="overflow-y-auto bg-media">
305
+ <v-container v-if="search" fluid>
306
+ <v-row v-for="(feed,index) in feeds" :key="feed.rssUrl" @click="listFeed(feed)">
307
+ <v-col cols="1">{{feed.country}}</v-col>
308
+ <v-col cols="2">{{feed.genre}}</v-col>
309
+ <v-col cols="2">{{feed.owner}}</v-col>
310
+ <v-col cols="5">{{feed.title}}</v-col>
311
+ </v-row>
312
+ </v-container>
313
+ <v-container v-else>
314
+ <v-row v-for="(item,index) in items" :key="item.rssUrl" @click="listItem(item)">
315
+ <v-col cols="1">{{item.title}}</v-col>
316
+ <v-col cols="2">{{toTime(item.duration)}}</v-col>
317
+ <v-col cols="2">{{feed.owner}}</v-col>
318
+ <v-col cols="5">{{item.title}}</v-col>
319
+ </v-row>
320
+ </v-container>
321
+ </v-card>
322
+
323
+ <!--
324
+ <v-card class="bg-media_head">
325
+ <audio ref="audio" preload="none" @ended="onEnded" @timeupdate="onTimeUpdate">
326
+ </audio>
327
+ <v-list-item lines="three">
328
+ <v-list-item-content>
329
+ <v-list-item-title text-h6>{{selected?.title}}</v-list-item-title>
330
+ <v-list-item-subtitle>{{toSubTitle(selected!)}}</v-list-item-subtitle>
331
+ </v-list-item-content>
332
+ <template v-slot:append>
333
+ <v-select v-model="currentPlayer" density="compact"
334
+ :items="playerNames"
335
+ persistent-hint
336
+ @update:modelValue="onPlayerChange"
337
+ hide-details single-line>
338
+ </v-select>
339
+ </template>
340
+ </v-list-item>
341
+ <v-card-actions>
342
+ <v-btn v-if="backVisible" @click="listBack">
343
+ <v-icon icon="$back" />
344
+ </v-btn>
345
+ <v-btn size="small" v-if="playingFeed" @click="playpause">
346
+ <v-icon v-if="paused" icon="$play" />
347
+ <v-icon v-else icon="$pause" />
348
+ </v-btn>
349
+ <v-btn size="small" v-if="playingFeed" @click="previous">
350
+ <v-icon icon="$prev" />
351
+ </v-btn>
352
+ <v-btn size="small" v-if="playingFeed" @click="next">
353
+ <v-icon icon="$next" />
354
+ </v-btn>
355
+ <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"
356
+ min="0" max="100" step="5" @update:modelValue="changeVolume">
357
+ </v-slider>
358
+ </v-card-actions>
359
+ <v-progress-linear v-if="playingFeed" v-model="positionLength" color="blue" height="25"><strong>{{positionText}}</strong></v-progress-linear>
360
+ </v-card>
361
+ <v-card :height="listHeight" density="compact" class="overflow-y-auto bg-media">
362
+ <v-list lines="two" class="bg-media">
363
+ <v-list-item-group v-model="itemIndex">
364
+ <v-list-item v-for="(feed,index) in feeds" :key="feed.title" density="compact" :title="feed.title" :subtitle="toSubTitle(feed)"
365
+ active-color="blue" :active="index == playIndex" @click="listItem(feed)">
366
+ </v-list-item>
367
+ </v-list-item-group>
368
+ </v-list>
369
+ </v-card>
370
+ -->
371
+ </template>
372
+
@@ -1,171 +0,0 @@
1
- <script setup lang="ts">
2
- import { inject, ref, reactive, computed, onMounted, onUnmounted, nextTick, watch, StyleValue } from 'vue';
3
- import type { ComponentPublicInstance } from 'vue';
4
- import { useRouter } from 'vue-router';
5
- import { IAppState, EScope, EDevice, appStateSymbol, appConfigSymbol } from '@christianriedl/utils';
6
- import { getPlaylistSymbol, IPlaylistService, IPlaylistState, IRssFeed } from '@christianriedl/media';
7
-
8
- const appState = inject(appStateSymbol)!;
9
- const getPlaylistService = inject(getPlaylistSymbol)!;
10
- const playlistService = getPlaylistService() ;
11
- const heightStyle = computed<StyleValue>(() => { return { height: appState.bodyHeight.value + 'px', overflowY: 'auto' } });
12
- const isMobile = appState.isMobile && (appState.device != EDevice.iPad);
13
- const router = useRouter();
14
- const feeds: IRssFeed[] = reactive([]);
15
- const selected = ref<IRssFeed|null>(null);
16
- const status = ref<IPlaylistState|null>(null);
17
- const audio = ref<HTMLAudioElement | null>(null);
18
- const listhead = ref<ComponentPublicInstance | null>(null);
19
- const listHeight = ref(0);
20
- const playerNames = ['Local', 'Soundbar'];
21
- const currentPlayer = ref<string>(playerNames[0]);
22
- const playLocal = computed(() => currentPlayer.value == 'Local');
23
- const playIndex = ref(-1);
24
- const playingFeed = ref<IRssFeed|null>(null);
25
- const position = ref(0);
26
- const positionLength = ref(0);
27
- let statusTimer = 0;
28
-
29
-
30
-
31
- onMounted(async () => {
32
- playlistService.log.info('OnlineBroadcast created');
33
- const f = await playlistService.getFeeds();
34
- feeds.splice(0, feeds.length, ...f);
35
- computeListHeight();
36
- });
37
- onUnmounted(() => {
38
- if (statusTimer > 0) {
39
- clearTimeout(statusTimer);
40
- statusTimer = 0;
41
- }
42
- });
43
-
44
- function computeListHeight() {
45
- nextTick(() => {
46
- let height = appState.bodyHeight.value;
47
- if (listhead.value) {
48
- height = height - (listhead.value.$el as HTMLElement).clientHeight;
49
- }
50
- listHeight.value = height;
51
- })
52
- }
53
- async function play() {
54
- if (playLocal.value) {
55
- audio.value!.src = selected.value.link;
56
- audio.value!.load();
57
- await audio.value!.play();
58
- playlistService.log.trace(`play local ${selected.value.title}`)
59
- }
60
- else {
61
- const rc = await playlistService.play(selected.value.link);
62
- if (!rc)
63
- return;
64
- await getStatus();
65
- playlistService.log.trace(`play remote ${selected.value.title}`)
66
- }
67
- playingFeed.value = selected.value;
68
- playIndex.value = feeds.findIndex((f) => f.link == selected.value.link);
69
- }
70
- function listItem(item: IRssFeed) {
71
- selected.value = item;
72
- computeListHeight();
73
- }
74
- function listBack() {
75
- router.back();
76
- }
77
- async function getStatus() {
78
- const status = await playlistService.getStatus();
79
- status.value = status;
80
- setPosition(status.position.toNumber());
81
- statusTimer = setTimeout(async () => getStatus(), 1000);
82
- }
83
-
84
- function toSubTitle(f: IRssFeed): string {
85
- if (!f.duration)
86
- return "";
87
- let s = f.duration;
88
- var r = "";
89
- s = Math.floor(s);
90
- if (s >= 3600) {
91
- r += Math.floor(s / 3600).toString() + ":"; s = s % 3600;
92
- }
93
- if (!r || s >= 600) {
94
- r += Math.floor(s / 60).toString() + ":"; s = s % 60;
95
- }
96
- else {
97
- r += "0"; r += Math.floor(s / 60).toString() + ":"; s = s % 60;
98
- }
99
- if (s >= 10) {
100
- r += s.toString(); s = -1;
101
- }
102
- else if (s >= 0) {
103
- r += "0"; r += s.toString();
104
- s = -1;
105
- }
106
- return r;
107
- }
108
- function onEnded() {
109
- }
110
- function onTimeUpdate() {
111
- setPosition(audio.value!.currentTime);
112
- }
113
- function setPosition(value: number) {
114
- position.value = value;
115
- if (playingFeed.value && playingFeed.value.duration > 0)
116
- positionLength.value = position.value * 100 / playingFeed.value.duration;
117
- else
118
- positionLength.value = 0;
119
- }
120
-
121
- </script>
122
-
123
- <template>
124
- <v-card ref="listhead" class="bg-media_head">
125
- <audio ref="audio" preload="none" @ended="onEnded" @timeupdate="onTimeUpdate">
126
- </audio>
127
- <v-list-item lines="three">
128
- <v-list-item-content>
129
- <v-list-item-title text-h6>{{selected.title}}</v-list-item-title>
130
- <v-list-item-subtitle>{{toSubTitle(selected)}}</v-list-item-subtitle>
131
- </v-list-item-content>
132
- <template v-slot:append>
133
- <v-select v-model="currentPlayer"
134
- :items="playerNames"
135
- persistent-hint
136
- @update:modelValue="onPlayerChange"
137
- hide-details single-line>
138
- </v-select>
139
- </template>
140
- </v-list-item>
141
- <v-card-actions>
142
- <v-btn v-if="backVisible" @click="listBack">
143
- <v-icon icon="$back" />
144
- </v-btn>
145
- <v-btn size="small" v-if="playingTrack" @click="playpause">
146
- <v-icon v-if="paused" icon="$play" />
147
- <v-icon v-else icon="$pause" />
148
- </v-btn>
149
- <v-btn size="small" v-if="playingTrack" @click="previous">
150
- <v-icon icon="$prev" />
151
- </v-btn>
152
- <v-btn size="small" v-if="playingTrack" @click="next">
153
- <v-icon icon="$next" />
154
- </v-btn>
155
- <v-slider v-if="!isLocal && playingTrack" style="width:30%" color="red" track-color="blue" track-fill-color="red" v-model="volume" hide-details density="compact"
156
- min="0" max="100" step="5" @update:modelValue="changeVolume">
157
- </v-slider>
158
- </v-card-actions>
159
- <v-progress-linear v-if="playingTrack" v-model="positionLength" color="blue" height="25"><strong>{{positionText}}</strong></v-progress-linear>
160
- </v-card>
161
- <v-card :height="listHeight" density="compact" class="overflow-y-auto bg-media">
162
- <v-list lines="two" class="bg-media">
163
- <v-list-item-group v-model="itemIndex">
164
- <v-list-item v-for="(item,index) in items" :key="item.title" density="compact" :title="item.title" :subtitle="toSubTitle(item)"
165
- active-color="blue" :active="index == playIndex" @click="listItem(item)">
166
- </v-list-item>
167
- </v-list-item-group>
168
- </v-list>
169
- </v-card>
170
- </template>
171
-