@christianriedl/media 1.0.1 → 1.0.4
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/playerService.d.ts +26 -7
- package/dist/playerService.js +35 -4
- package/dist/playerService.js.map +1 -1
- package/package.json +1 -1
- package/src/views/MusicPage.vue +159 -30
- package/src/views/PhotosPage.vue +1 -1
package/dist/playerService.d.ts
CHANGED
|
@@ -1,21 +1,36 @@
|
|
|
1
1
|
import { IRest } from '@christianriedl/rest';
|
|
2
2
|
import { ILogger, Dictionary } from '@christianriedl/utils';
|
|
3
|
+
import { EMediaType } from "./iMedia";
|
|
4
|
+
export declare enum EPlayState {
|
|
5
|
+
PowerOff = 0,
|
|
6
|
+
NoMedia = 1,
|
|
7
|
+
Paused = 2,
|
|
8
|
+
Stopped = 3,
|
|
9
|
+
Play = 4,
|
|
10
|
+
PlayAll = 5,
|
|
11
|
+
PlayOnly = 6
|
|
12
|
+
}
|
|
3
13
|
export interface IPlayerState {
|
|
4
14
|
playerName: string;
|
|
5
15
|
albumId: string;
|
|
6
16
|
mediaId: string;
|
|
7
17
|
trackNo: number;
|
|
8
18
|
duration: number;
|
|
9
|
-
state:
|
|
19
|
+
state: EPlayState;
|
|
10
20
|
volume: number;
|
|
11
21
|
mute: boolean;
|
|
12
22
|
}
|
|
13
23
|
export interface IPlayerRequest {
|
|
14
24
|
playerName: string;
|
|
15
|
-
folderId
|
|
16
|
-
mediaId
|
|
17
|
-
trackNo
|
|
18
|
-
withStreamTitle
|
|
25
|
+
folderId?: string;
|
|
26
|
+
mediaId?: string;
|
|
27
|
+
trackNo?: number;
|
|
28
|
+
withStreamTitle?: boolean;
|
|
29
|
+
}
|
|
30
|
+
export interface IPlayerCommand {
|
|
31
|
+
playerName: string;
|
|
32
|
+
cmd: string;
|
|
33
|
+
value: string;
|
|
19
34
|
}
|
|
20
35
|
export declare class PlayerService {
|
|
21
36
|
rest: IRest;
|
|
@@ -24,8 +39,12 @@ export declare class PlayerService {
|
|
|
24
39
|
playerNames: string[];
|
|
25
40
|
playerStates: Dictionary<IPlayerState>;
|
|
26
41
|
constructor(rest: IRest, log: ILogger);
|
|
27
|
-
getPlayers(): Promise<string[]>;
|
|
42
|
+
getPlayers(mediaType?: EMediaType, isReady?: boolean): Promise<string[]>;
|
|
28
43
|
getPlayerState(playerName: string): Promise<IPlayerState | null>;
|
|
29
44
|
play(playerRequest: IPlayerRequest): Promise<boolean>;
|
|
30
|
-
|
|
45
|
+
stop(playerName: string): Promise<boolean>;
|
|
46
|
+
pause(playerName: string): Promise<boolean>;
|
|
47
|
+
next(playerName: string): Promise<boolean>;
|
|
48
|
+
previous(playerName: string): Promise<boolean>;
|
|
49
|
+
command(playerCommand: IPlayerCommand): Promise<boolean>;
|
|
31
50
|
}
|
package/dist/playerService.js
CHANGED
|
@@ -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;
|
|
@@ -9,8 +19,13 @@ export class PlayerService {
|
|
|
9
19
|
this.rest = rest;
|
|
10
20
|
this.mediaUrl = rest.serviceUrl;
|
|
11
21
|
}
|
|
12
|
-
async getPlayers() {
|
|
13
|
-
const
|
|
22
|
+
async getPlayers(mediaType, isReady) {
|
|
23
|
+
const query = {};
|
|
24
|
+
if (mediaType)
|
|
25
|
+
query.mediaType = mediaType;
|
|
26
|
+
if (isReady)
|
|
27
|
+
query.isReady = isReady;
|
|
28
|
+
const players = await this.rest.getData('apiplayer/players', query);
|
|
14
29
|
this.playerNames = players.result;
|
|
15
30
|
return this.playerNames;
|
|
16
31
|
}
|
|
@@ -27,8 +42,24 @@ export class PlayerService {
|
|
|
27
42
|
const result = await this.rest.postData('apiplayer/play', playerRequest);
|
|
28
43
|
return result.ok;
|
|
29
44
|
}
|
|
30
|
-
async
|
|
31
|
-
const result = await this.rest.postData('apiplayer/playstop'
|
|
45
|
+
async stop(playerName) {
|
|
46
|
+
const result = await this.rest.postData('apiplayer/playstop/' + playerName);
|
|
47
|
+
return result.ok;
|
|
48
|
+
}
|
|
49
|
+
async pause(playerName) {
|
|
50
|
+
const result = await this.rest.postData('apiplayer/playpause/' + playerName);
|
|
51
|
+
return result.ok;
|
|
52
|
+
}
|
|
53
|
+
async next(playerName) {
|
|
54
|
+
const result = await this.rest.postData('apiplayer/playnext/' + playerName);
|
|
55
|
+
return result.ok;
|
|
56
|
+
}
|
|
57
|
+
async previous(playerName) {
|
|
58
|
+
const result = await this.rest.postData('apiplayer/playprevious/' + playerName);
|
|
59
|
+
return result.ok;
|
|
60
|
+
}
|
|
61
|
+
async command(playerCommand) {
|
|
62
|
+
const result = await this.rest.postData('apiplayer/playcommand', playerCommand);
|
|
32
63
|
return result.ok;
|
|
33
64
|
}
|
|
34
65
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"playerService.js","sourceRoot":"","sources":["../src/playerService.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"playerService.js","sourceRoot":"","sources":["../src/playerService.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/src/views/MusicPage.vue
CHANGED
|
@@ -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.value == '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,10 @@
|
|
|
35
42
|
}
|
|
36
43
|
return '';
|
|
37
44
|
});
|
|
45
|
+
let syncTimer = 0;
|
|
46
|
+
let cmdTimer = 0;
|
|
47
|
+
let syncRemote = false;
|
|
48
|
+
|
|
38
49
|
window.addEventListener('popstate', onPopState);
|
|
39
50
|
function onPopState (event: any) {
|
|
40
51
|
if (event.state && event.state.noBackExitsApp && backVisible.value) {
|
|
@@ -59,6 +70,8 @@
|
|
|
59
70
|
selected.value = root;
|
|
60
71
|
items.splice(0, items.length, ...root.Folders);
|
|
61
72
|
computeListHeight();
|
|
73
|
+
const players = await playerService.getPlayers(EMediaType.Audio, true);
|
|
74
|
+
playerNames.splice(playerNames.length, 0, ...players);
|
|
62
75
|
})
|
|
63
76
|
watch(appState.bodyHeight, () => computeListHeight());
|
|
64
77
|
function computeListHeight() {
|
|
@@ -97,24 +110,68 @@
|
|
|
97
110
|
}
|
|
98
111
|
}
|
|
99
112
|
function listBack() {
|
|
113
|
+
syncRemote = false;
|
|
114
|
+
playingTrack.value = null;
|
|
100
115
|
const parent = mediaService.folders[selected.value.DLNAParentID];
|
|
101
116
|
selected.value = parent;
|
|
102
117
|
items.splice(0, items.length, ...parent.Folders);
|
|
103
118
|
computeListHeight();
|
|
104
119
|
}
|
|
105
120
|
function onTimeUpdate() {
|
|
106
|
-
|
|
121
|
+
setPosition(audio.value!.currentTime);
|
|
122
|
+
}
|
|
123
|
+
function setPosition(value: number) {
|
|
124
|
+
position.value = value;
|
|
107
125
|
if (playingTrack.value && playingTrack.value.Duration > 0)
|
|
108
126
|
positionLength.value = position.value * 100 / playingTrack.value.Duration;
|
|
109
127
|
else
|
|
110
128
|
positionLength.value = 0;
|
|
111
129
|
}
|
|
130
|
+
function onPlayerChange() {
|
|
131
|
+
if (syncTimer) {
|
|
132
|
+
window.clearTimeout(syncTimer);
|
|
133
|
+
syncTimer = 0;
|
|
134
|
+
}
|
|
135
|
+
if (!isLocal.value) {
|
|
136
|
+
syncTimer = window.setTimeout(() => getPlayerState(), 1000);
|
|
137
|
+
syncRemote = true;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
async function getPlayerState() {
|
|
141
|
+
syncTimer = 0;
|
|
142
|
+
const playerState = await playerService.getPlayerState(currentPlayer.value);
|
|
143
|
+
playerState.state = Helper.stringToEnum(EPlayState, playerState.state);
|
|
144
|
+
paused.value = playerState.state == EPlayState.Paused || playerState.state == EPlayState.Stopped;
|
|
145
|
+
if (!cmdTimer) {
|
|
146
|
+
volume.value = playerState.volume;
|
|
147
|
+
mute.value = playerState.mute;
|
|
148
|
+
}
|
|
149
|
+
if (syncRemote) {
|
|
150
|
+
if (!selected.value || playerState.folderId != selected.value.DLNAID) {
|
|
151
|
+
const album = mediaService.folders[playerState.albumId];
|
|
152
|
+
if (album) {
|
|
153
|
+
const audios = await mediaService.getAudios(album);
|
|
154
|
+
selected.value = audios;
|
|
155
|
+
items.splice(0, items.length, ...audios.Files);
|
|
156
|
+
playItems.splice(0, items.length, ...audios.Files);
|
|
157
|
+
if (playerState.mediaId) {
|
|
158
|
+
playIndex.value = items.findIndex((it) => it.DLNAID == playerState.mediaId);
|
|
159
|
+
if (playIndex.value >= 0)
|
|
160
|
+
playingTrack.value = items[playIndex.value];
|
|
161
|
+
}
|
|
162
|
+
computeListHeight();
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
setPosition(playerState.duration);
|
|
167
|
+
syncTimer = window.setTimeout(() => getPlayerState(), 1000);
|
|
168
|
+
}
|
|
112
169
|
function onEnded() {
|
|
113
170
|
mediaService.log.trace(`onEnded ${playIndex} ended`);
|
|
114
171
|
if (playIndex.value < playItems.length - 1)
|
|
115
172
|
play(playIndex.value + 1);
|
|
116
173
|
}
|
|
117
|
-
function play(index: number) {
|
|
174
|
+
async function play(index: number) {
|
|
118
175
|
if (items.length == playItems.length && items[0].DLNAParentID == playItems[0].DLNAParentID)
|
|
119
176
|
itemIndex.value = index;
|
|
120
177
|
playIndex.value = index;
|
|
@@ -122,29 +179,67 @@
|
|
|
122
179
|
paused.value = false;
|
|
123
180
|
playingTrack.value = item;
|
|
124
181
|
playingTrackUrl.value = mediaService.getAudioUrl(item);
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
.
|
|
182
|
+
if (isLocal.value) {
|
|
183
|
+
audio.value!.src = playingTrackUrl.value;
|
|
184
|
+
audio.value!.load();
|
|
185
|
+
audio.value!.play()
|
|
186
|
+
.then(() => { mediaService.log.trace(`play ${index}ended`) });
|
|
187
|
+
}
|
|
188
|
+
else {
|
|
189
|
+
const rc = await playerService.play({
|
|
190
|
+
playerName: currentPlayer.value,
|
|
191
|
+
folderId: selected.value.DLNAID,
|
|
192
|
+
mediaId: playingTrack.value.DLNAID,
|
|
193
|
+
trackNo: playingTrack.value.TrackNo
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
async function playFolder() {
|
|
198
|
+
const rc = await playerService.play({
|
|
199
|
+
playerName: currentPlayer.value,
|
|
200
|
+
folderId: selected.value.DLNAID,
|
|
201
|
+
trackNo: -1
|
|
202
|
+
});
|
|
203
|
+
syncRemote = true;
|
|
129
204
|
}
|
|
130
205
|
function playpause() {
|
|
131
|
-
if (
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
.
|
|
206
|
+
if (isLocal.value) {
|
|
207
|
+
if (audio.value!.paused) {
|
|
208
|
+
paused.value = false;
|
|
209
|
+
audio.value!.play()
|
|
210
|
+
.then(() => { console.log("play ended") });
|
|
211
|
+
}
|
|
212
|
+
else {
|
|
213
|
+
paused.value = true;
|
|
214
|
+
audio.value!.pause();
|
|
215
|
+
}
|
|
135
216
|
}
|
|
136
217
|
else {
|
|
137
|
-
paused.value
|
|
138
|
-
|
|
218
|
+
if (paused.value) {
|
|
219
|
+
playerService.play({ playerName: currentPlayer.value });
|
|
220
|
+
}
|
|
221
|
+
else {
|
|
222
|
+
playerService.pause(currentPlayer.value);
|
|
223
|
+
}
|
|
139
224
|
}
|
|
140
225
|
}
|
|
141
226
|
function previous() {
|
|
142
|
-
if (
|
|
143
|
-
|
|
227
|
+
if (isLocal.value) {
|
|
228
|
+
if (playIndex.value > 0)
|
|
229
|
+
play(playIndex.value - 1);
|
|
230
|
+
}
|
|
231
|
+
else {
|
|
232
|
+
playerService.previous(currentPlayer.value);
|
|
233
|
+
}
|
|
144
234
|
}
|
|
145
235
|
function next() {
|
|
146
|
-
if (
|
|
147
|
-
|
|
236
|
+
if (isLocal.value) {
|
|
237
|
+
if (playIndex.value < playItems.length - 1)
|
|
238
|
+
play(playIndex.value + 1);
|
|
239
|
+
}
|
|
240
|
+
else {
|
|
241
|
+
playerService.next(currentPlayer.value);
|
|
242
|
+
}
|
|
148
243
|
}
|
|
149
244
|
function download() {
|
|
150
245
|
window.open(playingTrackUrl.value);
|
|
@@ -153,6 +248,19 @@
|
|
|
153
248
|
const downloadUrl = mediaService.getAlbumDownloadUrl(selected.value.Url);
|
|
154
249
|
window.open(downloadUrl);
|
|
155
250
|
}
|
|
251
|
+
function changeVolume() {
|
|
252
|
+
if (cmdTimer)
|
|
253
|
+
window.clearTimeout(cmdTimer);
|
|
254
|
+
|
|
255
|
+
cmdTimer = window.setTimeout(async () => {
|
|
256
|
+
await playerService.command({
|
|
257
|
+
playerName: currentPlayer.value,
|
|
258
|
+
cmd: "Volume",
|
|
259
|
+
value: volume.value.toString()
|
|
260
|
+
});
|
|
261
|
+
cmdTimer = 0;
|
|
262
|
+
}, 500);
|
|
263
|
+
}
|
|
156
264
|
function to_time(s: number): string {
|
|
157
265
|
var r = "";
|
|
158
266
|
s = Math.floor(s);
|
|
@@ -181,14 +289,28 @@
|
|
|
181
289
|
<audio ref="audio" preload="none" @ended="onEnded" @timeupdate="onTimeUpdate">
|
|
182
290
|
</audio>
|
|
183
291
|
<v-list-item three-line>
|
|
184
|
-
<v-
|
|
185
|
-
<
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
<v-
|
|
191
|
-
|
|
292
|
+
<v-row>
|
|
293
|
+
<v-col cols="1">
|
|
294
|
+
<v-list-item-avatar tile rounded="0" size="x-large" v-if="selected.ThumbnailUrl">
|
|
295
|
+
<img width="60" :src="selected.ThumbnailUrl">
|
|
296
|
+
</v-list-item-avatar>
|
|
297
|
+
</v-col>
|
|
298
|
+
<v-col cols="7">
|
|
299
|
+
<v-list-item-content>
|
|
300
|
+
<v-list-item-title>{{selected.title}}</v-list-item-title>
|
|
301
|
+
<v-list-item-subtitle>{{selected.subTitle}}</v-list-item-subtitle>
|
|
302
|
+
<v-list-item-subtitle v-if="playingTrack">{{playingTrack.Name}}</v-list-item-subtitle>
|
|
303
|
+
</v-list-item-content>
|
|
304
|
+
</v-col>
|
|
305
|
+
<v-col cols="4">
|
|
306
|
+
<v-select v-model="currentPlayer"
|
|
307
|
+
:items="playerNames"
|
|
308
|
+
persistent-hint
|
|
309
|
+
@update:modelValue="onPlayerChange"
|
|
310
|
+
dense solo hide-details single-line>
|
|
311
|
+
</v-select>
|
|
312
|
+
</v-col>
|
|
313
|
+
</v-row>
|
|
192
314
|
</v-list-item>
|
|
193
315
|
<v-card-actions>
|
|
194
316
|
<v-btn v-if="backVisible" @click="listBack">
|
|
@@ -198,20 +320,27 @@
|
|
|
198
320
|
Album
|
|
199
321
|
<v-icon>{{$vuetify.icons.values.download}}</v-icon>
|
|
200
322
|
</v-btn>
|
|
201
|
-
<v-btn
|
|
323
|
+
<v-btn v-if="!isLocal" @click="playFolder">
|
|
324
|
+
{{selected.title}}
|
|
325
|
+
<v-icon>{{$vuetify.icons.values.play}}</v-icon>
|
|
326
|
+
</v-btn>
|
|
327
|
+
<v-btn small v-if="playingTrack" @click="playpause">
|
|
202
328
|
<v-icon>{{paused ? $vuetify.icons.values.play : $vuetify.icons.values.pause }}</v-icon>
|
|
203
329
|
</v-btn>
|
|
204
|
-
<v-btn small v-if="
|
|
330
|
+
<v-btn small v-if="playingTrack" @click="previous">
|
|
205
331
|
<v-icon>{{$vuetify.icons.values.prev}}</v-icon>
|
|
206
332
|
</v-btn>
|
|
207
|
-
<v-btn small v-if="
|
|
333
|
+
<v-btn small v-if="playingTrack" @click="next">
|
|
208
334
|
<v-icon>{{$vuetify.icons.values.next}}</v-icon>
|
|
209
335
|
</v-btn>
|
|
210
336
|
<v-btn small v-if="downloadVisible" @click="download">
|
|
211
337
|
<v-icon>{{$vuetify.icons.values.download}}</v-icon>
|
|
212
338
|
</v-btn>
|
|
339
|
+
<v-slider v-if="playingTrack" style="width:30%" color="red" track-color="blue" track-fill-color="red" v-model="volume" hide-details density="compact"
|
|
340
|
+
min="0" max="100" step="5" @update:modelValue="changeVolume">
|
|
341
|
+
</v-slider>
|
|
213
342
|
</v-card-actions>
|
|
214
|
-
<v-progress-linear v-if="
|
|
343
|
+
<v-progress-linear v-if="playingTrack" v-model="positionLength" color="blue" height="25"><strong>{{positionText}}</strong></v-progress-linear>
|
|
215
344
|
</v-card>
|
|
216
345
|
<v-card :height="listHeight" class="overflow-y-auto bg-media">
|
|
217
346
|
<v-list two-line class="bg-media">
|
package/src/views/PhotosPage.vue
CHANGED
|
@@ -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"
|
|
163
|
+
<v-select v-model="selected.root"
|
|
164
164
|
:items="roots"
|
|
165
165
|
persistent-hint
|
|
166
166
|
@update:modelValue="onRootChange"
|