@christianriedl/media 1.0.1 → 1.0.2

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.
@@ -1,21 +1,30 @@
1
1
  import { IRest } from '@christianriedl/rest';
2
2
  import { ILogger, Dictionary } from '@christianriedl/utils';
3
+ export declare enum EPlayState {
4
+ PowerOff = 0,
5
+ NoMedia = 1,
6
+ Paused = 2,
7
+ Stopped = 3,
8
+ Play = 4,
9
+ PlayAll = 5,
10
+ PlayOnly = 6
11
+ }
3
12
  export interface IPlayerState {
4
13
  playerName: string;
5
14
  albumId: string;
6
15
  mediaId: string;
7
16
  trackNo: number;
8
17
  duration: number;
9
- state: string;
18
+ state: EPlayState;
10
19
  volume: number;
11
20
  mute: boolean;
12
21
  }
13
22
  export interface IPlayerRequest {
14
23
  playerName: string;
15
- folderId: string;
16
- mediaId: string;
17
- trackNo: number;
18
- withStreamTitle: boolean;
24
+ folderId?: string;
25
+ mediaId?: string;
26
+ trackNo?: number;
27
+ withStreamTitle?: boolean;
19
28
  }
20
29
  export declare class PlayerService {
21
30
  rest: IRest;
@@ -28,4 +37,5 @@ export declare class PlayerService {
28
37
  getPlayerState(playerName: string): Promise<IPlayerState | null>;
29
38
  play(playerRequest: IPlayerRequest): Promise<boolean>;
30
39
  playStop(playerName: string): Promise<boolean>;
40
+ playPause(playerName: string): Promise<boolean>;
31
41
  }
@@ -1,3 +1,13 @@
1
+ export var EPlayState;
2
+ (function (EPlayState) {
3
+ EPlayState[EPlayState["PowerOff"] = 0] = "PowerOff";
4
+ EPlayState[EPlayState["NoMedia"] = 1] = "NoMedia";
5
+ EPlayState[EPlayState["Paused"] = 2] = "Paused";
6
+ EPlayState[EPlayState["Stopped"] = 3] = "Stopped";
7
+ EPlayState[EPlayState["Play"] = 4] = "Play";
8
+ EPlayState[EPlayState["PlayAll"] = 5] = "PlayAll";
9
+ EPlayState[EPlayState["PlayOnly"] = 6] = "PlayOnly";
10
+ })(EPlayState || (EPlayState = {}));
1
11
  export class PlayerService {
2
12
  rest;
3
13
  mediaUrl;
@@ -31,5 +41,9 @@ export class PlayerService {
31
41
  const result = await this.rest.postData('apiplayer/playstop', { playerName: playerName });
32
42
  return result.ok;
33
43
  }
44
+ async playPause(playerName) {
45
+ const result = await this.rest.postData('apiplayer/playpause', { playerName: playerName });
46
+ return result.ok;
47
+ }
34
48
  }
35
49
  //# sourceMappingURL=playerService.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"playerService.js","sourceRoot":"","sources":["../src/playerService.ts"],"names":[],"mappings":"AA0BA,MAAM,OAAO,aAAa;IACtB,IAAI,CAAQ;IACZ,QAAQ,CAAS;IACjB,GAAG,CAAU;IACb,WAAW,GAAa,EAAE,CAAC;IAC3B,YAAY,GAA6B,EAAE,CAAA;IAE3C,YAAY,IAAW,EAAE,GAAY;QACjC,IAAI,CAAC,GAAG,GAAG,GAAG,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,UAAU,CAAC;IACpC,CAAC;IACD,KAAK,CAAC,UAAU;QACZ,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,OAAO,CAAW,mBAAmB,CAAC,CAAC;QACvE,IAAI,CAAC,WAAW,GAAG,OAAO,CAAC,MAAkB,CAAC;QAC9C,OAAO,IAAI,CAAC,WAAW,CAAC;IAC5B,CAAC;IACD,KAAK,CAAC,cAAc,CAAC,UAAkB;QACnC,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,OAAO,CAAe,qBAAqB,EAAE,EAAE,UAAU,EAAE,UAAU,EAAC,CAAC,CAAC;QACvG,IAAI,MAAM,CAAC,EAAE,EAAE;YACX,MAAM,WAAW,GAAG,MAAM,CAAC,MAAsB,CAAC;YAClD,IAAI,CAAC,YAAY,CAAC,UAAU,CAAC,GAAG,WAAW,CAAC;YAC5C,OAAO,WAAW,CAAC;SACtB;QACD,OAAO,IAAI,CAAC;IAChB,CAAC;IACD,KAAK,CAAC,IAAI,CAAC,aAA6B;QACpC,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAiB,gBAAgB,EAAE,aAAa,CAAC,CAAC;QACzF,OAAO,MAAM,CAAC,EAAE,CAAC;IACrB,CAAC;IACD,KAAK,CAAC,QAAQ,CAAC,UAAkB;QAC7B,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAM,oBAAoB,EAAE,EAAE,UAAU,EAAE,UAAU,EAAE,CAAC,CAAC;QAC/F,OAAO,MAAM,CAAC,EAAE,CAAC;IACrB,CAAC;CACJ"}
1
+ {"version":3,"file":"playerService.js","sourceRoot":"","sources":["../src/playerService.ts"],"names":[],"mappings":"AAOA,MAAM,CAAN,IAAY,UAQX;AARD,WAAY,UAAU;IAClB,mDAAY,CAAA;IACZ,iDAAO,CAAA;IACP,+CAAM,CAAA;IACN,iDAAO,CAAA;IACP,2CAAI,CAAA;IACJ,iDAAO,CAAA;IACP,mDAAQ,CAAA;AACZ,CAAC,EARW,UAAU,KAAV,UAAU,QAQrB;AAqBD,MAAM,OAAO,aAAa;IACtB,IAAI,CAAQ;IACZ,QAAQ,CAAS;IACjB,GAAG,CAAU;IACb,WAAW,GAAa,EAAE,CAAC;IAC3B,YAAY,GAA6B,EAAE,CAAA;IAE3C,YAAY,IAAW,EAAE,GAAY;QACjC,IAAI,CAAC,GAAG,GAAG,GAAG,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,UAAU,CAAC;IACpC,CAAC;IACD,KAAK,CAAC,UAAU;QACZ,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,OAAO,CAAW,mBAAmB,CAAC,CAAC;QACvE,IAAI,CAAC,WAAW,GAAG,OAAO,CAAC,MAAkB,CAAC;QAC9C,OAAO,IAAI,CAAC,WAAW,CAAC;IAC5B,CAAC;IACD,KAAK,CAAC,cAAc,CAAC,UAAkB;QACnC,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,OAAO,CAAe,qBAAqB,EAAE,EAAE,UAAU,EAAE,UAAU,EAAC,CAAC,CAAC;QACvG,IAAI,MAAM,CAAC,EAAE,EAAE;YACX,MAAM,WAAW,GAAG,MAAM,CAAC,MAAsB,CAAC;YAClD,IAAI,CAAC,YAAY,CAAC,UAAU,CAAC,GAAG,WAAW,CAAC;YAC5C,OAAO,WAAW,CAAC;SACtB;QACD,OAAO,IAAI,CAAC;IAChB,CAAC;IACD,KAAK,CAAC,IAAI,CAAC,aAA6B;QACpC,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAiB,gBAAgB,EAAE,aAAa,CAAC,CAAC;QACzF,OAAO,MAAM,CAAC,EAAE,CAAC;IACrB,CAAC;IACD,KAAK,CAAC,QAAQ,CAAC,UAAkB;QAC7B,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAM,oBAAoB,EAAE,EAAE,UAAU,EAAE,UAAU,EAAE,CAAC,CAAC;QAC/F,OAAO,MAAM,CAAC,EAAE,CAAC;IACrB,CAAC;IACD,KAAK,CAAC,SAAS,CAAC,UAAkB;QAC9B,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAM,qBAAqB,EAAE,EAAE,UAAU,EAAE,UAAU,EAAE,CAAC,CAAC;QAChG,OAAO,MAAM,CAAC,EAAE,CAAC;IACrB,CAAC;CACJ"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@christianriedl/media",
3
- "version": "1.0.1",
3
+ "version": "1.0.2",
4
4
  "description": "RIC media interfaces",
5
5
 
6
6
  "main": "dist/index.js",
@@ -1,14 +1,18 @@
1
1
  <script setup lang="ts">
2
2
  import { inject, ref, reactive, computed, onMounted, onUnmounted, nextTick, watch } from 'vue';
3
- import { IAppState } from '@christianriedl/utils';
4
- import { EItemType, EMediaType, IMediaFolder, IMediaItem, IAudioFile, MediaService } from '@christianriedl/media';
3
+ import { IAppState, Helper } from '@christianriedl/utils';
4
+ import { EItemType, EMediaType, IMediaFolder, IMediaItem, IAudioFile, MediaService, EPlayState, PlayerService } from '@christianriedl/media';
5
5
 
6
6
  const appState = inject<IAppState>('appstate')!;
7
7
  const getMediaService = inject<() => MediaService>('get-media')!;
8
8
  const mediaService = getMediaService();
9
+ const getPlayerService = inject<() => PlayerService>('get-player')!;
10
+ const playerService = getPlayerService();
9
11
  const heightStyle = computed(() => { return { height: appState.bodyHeight.value + 'px', overflowY: 'auto' } });
10
12
  const isMobile = appState.isMobile;
11
13
 
14
+ const playerNames = reactive<string[]>(['Local']);
15
+ const currentPlayer = ref('Local');
12
16
  const items: IMediaItem[] = reactive([]);
13
17
  const selected = ref<IMediaItem>({ Name: 'Root', ItemType: EItemType.Root } as IMediaFolder);
14
18
  const playingTrackUrl = ref("");
@@ -19,7 +23,10 @@
19
23
  const positionLength = ref(0);
20
24
  const listHeight = ref(0);
21
25
  const playIndex = ref(-1);
26
+ const volume = ref(0);
27
+ const mute = ref(false);
22
28
 
29
+ const isLocal = computed(() => currentPlayer == 'Local');
23
30
  const backVisible = computed(() => selected.value.ItemType != EItemType.AudioRoot);
24
31
  const downloadAlbumVisible = computed(() => selected.value.ItemType == EItemType.AudioAlbum && !playingTrackUrl.value && !isMobile);
25
32
  const downloadVisible = computed(() => playingTrackUrl.value && !isMobile);
@@ -35,6 +42,8 @@
35
42
  }
36
43
  return '';
37
44
  });
45
+ let timer = 0;
46
+
38
47
  window.addEventListener('popstate', onPopState);
39
48
  function onPopState (event: any) {
40
49
  if (event.state && event.state.noBackExitsApp && backVisible.value) {
@@ -59,6 +68,8 @@
59
68
  selected.value = root;
60
69
  items.splice(0, items.length, ...root.Folders);
61
70
  computeListHeight();
71
+ const players = await playerService.getPlayers();
72
+ playerNames.splice(playerNames.length, 0, ...players);
62
73
  })
63
74
  watch(appState.bodyHeight, () => computeListHeight());
64
75
  function computeListHeight() {
@@ -109,12 +120,46 @@
109
120
  else
110
121
  positionLength.value = 0;
111
122
  }
123
+ function onPlayerChange() {
124
+ if (timer) {
125
+ window.clearTimeout(timer);
126
+ timer = 0;
127
+ }
128
+ if (currentPlayer != 'Local') {
129
+ timer = window.setTimeout(getPlayerState, 1000);
130
+ }
131
+ }
132
+ async function getPlayerState() {
133
+ const playerState = await playerService.getPlayerState(currentPlayer);
134
+ playerState.state = Helper.stringToEnum(EPlayState, playerState.state);
135
+ paused.value = playerState.state == EPlayState.Paused;
136
+ volume.value = playerState.volume;
137
+ mute.value = playerState.mute;
138
+ if (!selected.value || playerState.folderId != selected.value.DLNAID) {
139
+ const album = mediaService.folders[playerState.folderId];
140
+ if (album) {
141
+ const audios = mediaService.getAudios(selected.Value);
142
+ selected.value = audios;
143
+ items.splice(0, items.length, ...audios.Files);
144
+ playItems.splice(0, items.length, ...audios.Files);
145
+ if (playerState.mediaId) {
146
+ playIndex.value = items.findIndex((it) => it.DLNAID == playerState.mediaId);
147
+ if (playIndex.value >= 0) {
148
+ playingTrack.value = items[playIndex.value];
149
+ position.value = playingTrack.value.duration * playerState.duration / 100;
150
+ }
151
+ }
152
+ computeListHeight();
153
+ }
154
+ }
155
+ timer = window.setTimeout(getPlayerState, 1000);
156
+ }
112
157
  function onEnded() {
113
158
  mediaService.log.trace(`onEnded ${playIndex} ended`);
114
159
  if (playIndex.value < playItems.length - 1)
115
160
  play(playIndex.value + 1);
116
161
  }
117
- function play(index: number) {
162
+ async function play(index: number) {
118
163
  if (items.length == playItems.length && items[0].DLNAParentID == playItems[0].DLNAParentID)
119
164
  itemIndex.value = index;
120
165
  playIndex.value = index;
@@ -122,20 +167,38 @@
122
167
  paused.value = false;
123
168
  playingTrack.value = item;
124
169
  playingTrackUrl.value = mediaService.getAudioUrl(item);
125
- audio.value!.src = playingTrackUrl.value;
126
- audio.value!.load();
127
- audio.value!.play()
128
- .then(() => { mediaService.log.trace(`play ${index}ended`) });
170
+ if (isLocal) {
171
+ audio.value!.src = playingTrackUrl.value;
172
+ audio.value!.load();
173
+ audio.value!.play()
174
+ .then(() => { mediaService.log.trace(`play ${index}ended`) });
175
+ }
176
+ else {
177
+ const rc = await playerService.play({
178
+ playerName: currentPlayer,
179
+ folderId: selected.value.DLNAID,
180
+ mediaId: playingTrack.value.DLNAID,
181
+ trackNo: playingTrack.value.TrackNo
182
+ });
183
+ }
129
184
  }
130
185
  function playpause() {
131
- if (audio.value!.paused) {
132
- paused.value = false;
133
- audio.value!.play()
134
- .then(() => { console.log("play ended") });
186
+ if (isLocal) {
187
+ if (audio.value!.paused) {
188
+ paused.value = false;
189
+ audio.value!.play()
190
+ .then(() => { console.log("play ended") });
191
+ }
192
+ else {
193
+ paused.value = true;
194
+ audio.value!.pause();
195
+ }
196
+ }
197
+ if (paused.value) {
198
+ playerService.play({ playerName: currentPlayer });
135
199
  }
136
200
  else {
137
- paused.value = true;
138
- audio.value!.pause();
201
+ playerService.pause(currentPlayer);
139
202
  }
140
203
  }
141
204
  function previous() {
@@ -181,14 +244,28 @@
181
244
  <audio ref="audio" preload="none" @ended="onEnded" @timeupdate="onTimeUpdate">
182
245
  </audio>
183
246
  <v-list-item three-line>
184
- <v-list-item-avatar tile rounded="0" size="x-large" v-if="selected.ThumbnailUrl">
185
- <img width="40" :src="selected.ThumbnailUrl">
186
- </v-list-item-avatar>
187
- <v-list-item-content>
188
- <v-list-item-title>{{selected.title}}</v-list-item-title>
189
- <v-list-item-subtitle>{{selected.subTitle}}</v-list-item-subtitle>
190
- <v-list-item-subtitle v-if="playingTrack">{{playingTrack.Name}}</v-list-item-subtitle>
191
- </v-list-item-content>
247
+ <v-row>
248
+ <v-col cols="4">
249
+ <v-select v-model="currentPlayer"
250
+ :items="playerNames"
251
+ persistent-hint
252
+ @update:modelValue="onPlayerChange"
253
+ dense solo hide-details single-line>
254
+ </v-select>
255
+ </v-col>
256
+ <v-col cols="2">
257
+ <v-list-item-avatar tile rounded="0" size="x-large" v-if="selected.ThumbnailUrl">
258
+ <img width="40" :src="selected.ThumbnailUrl">
259
+ </v-list-item-avatar>
260
+ </v-col>
261
+ <v-col cols="6">
262
+ <v-list-item-content>
263
+ <v-list-item-title>{{selected.title}}</v-list-item-title>
264
+ <v-list-item-subtitle>{{selected.subTitle}}</v-list-item-subtitle>
265
+ <v-list-item-subtitle v-if="playingTrack">{{playingTrack.Name}}</v-list-item-subtitle>
266
+ </v-list-item-content>
267
+ </v-col>
268
+ </v-row>
192
269
  </v-list-item>
193
270
  <v-card-actions>
194
271
  <v-btn v-if="backVisible" @click="listBack">
@@ -160,7 +160,7 @@
160
160
  <v-icon large>{{$vuetify.icons.values.back}}</v-icon>Back
161
161
  </v-btn>
162
162
  <v-rating clearable length="2" v-model="selected.rating" @update:modelValue="onRating" />
163
- <v-select v-model="selected.root" c
163
+ <v-select v-model="selected.root"
164
164
  :items="roots"
165
165
  persistent-hint
166
166
  @update:modelValue="onRootChange"