@christianriedl/media 1.0.286 → 1.0.287
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/package.json
CHANGED
|
@@ -2,9 +2,7 @@
|
|
|
2
2
|
import { ref, computed } from 'vue';
|
|
3
3
|
import { IFullBook } from '@christianriedl/media';
|
|
4
4
|
const props = defineProps<{ book: IFullBook, selected?: boolean, ismobile?: boolean }>();
|
|
5
|
-
const emits = defineEmits<{
|
|
6
|
-
(e: 'selectbook'): void
|
|
7
|
-
}>();
|
|
5
|
+
const emits = defineEmits<{ (e: 'selectbook'): void, (e: 'info'): void }>();
|
|
8
6
|
const book = props.book;
|
|
9
7
|
const classObject = computed(() => ({
|
|
10
8
|
'bg-secondary': props.selected
|
|
@@ -12,6 +10,9 @@
|
|
|
12
10
|
function onSelect () {
|
|
13
11
|
emits ("selectbook");
|
|
14
12
|
}
|
|
13
|
+
function onInfo() {
|
|
14
|
+
emits("info");
|
|
15
|
+
}
|
|
15
16
|
</script>
|
|
16
17
|
|
|
17
18
|
<template>
|
|
@@ -24,12 +25,18 @@
|
|
|
24
25
|
</v-col>
|
|
25
26
|
</v-row>
|
|
26
27
|
<v-row v-else density="compact" class="align-center" :class="classObject" @click="onSelect" gap="0">
|
|
27
|
-
<v-col cols="
|
|
28
|
+
<v-col cols="3" >
|
|
28
29
|
<p class="font-weight-bold">{{book.givenName + ' ' + book.surName}}</p>
|
|
29
30
|
</v-col>
|
|
30
|
-
<v-col cols="
|
|
31
|
+
<v-col cols="7" >
|
|
31
32
|
<p>{{book.title}}</p>
|
|
32
33
|
</v-col>
|
|
34
|
+
<v-col cols="1">
|
|
35
|
+
<p>{{book.year}}</p>
|
|
36
|
+
</v-col>
|
|
37
|
+
<v-col cols="1">
|
|
38
|
+
<v-btn class="bg-office" variant="flat" icon="$info" @click.stop.prevent="onInfo"></v-btn>
|
|
39
|
+
</v-col>
|
|
33
40
|
</v-row>
|
|
34
41
|
<v-divider></v-divider>
|
|
35
42
|
</template>
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { inject, ref, reactive, computed, StyleValue } from 'vue';
|
|
3
3
|
import { IAppState, appStateSymbol, EDevice } from '@christianriedl/utils';
|
|
4
|
-
import { BooksService, getBooksSymbol, IAuthorShort, IAuthor, IFullBook } from '@christianriedl/media';
|
|
4
|
+
import { BooksService, getBooksSymbol, IAuthorShort, IAuthor, IBook, IFullBook } from '@christianriedl/media';
|
|
5
5
|
import BookSearchLine from '../components/BookSearchLine.vue';
|
|
6
|
+
import Microphone from '@christianriedl/utils/src/components/Microphone.vue';
|
|
6
7
|
|
|
7
8
|
const appState = inject(appStateSymbol)!;
|
|
8
9
|
const getBooksService = inject(getBooksSymbol)!;
|
|
@@ -10,6 +11,7 @@
|
|
|
10
11
|
const authors = reactive<IAuthorShort[]>([]);
|
|
11
12
|
const books = ref<IFullBook[]>([]);
|
|
12
13
|
const selectedBook = ref<IFullBook|null>(null);
|
|
14
|
+
const showBook = ref<IBook | null>(null);
|
|
13
15
|
const selected = ref<IAuthorShort>({ id:0, gN: "", sN: "" });
|
|
14
16
|
const title = ref("");
|
|
15
17
|
const rating = ref(0);
|
|
@@ -17,6 +19,8 @@
|
|
|
17
19
|
const content = ref("");
|
|
18
20
|
let isMobile = appState.isMobile && (appState.device != EDevice.iPad);
|
|
19
21
|
const heightStyle = computed<StyleValue>(() => { return { height: appState.bodyHeight.value + "px", overflowY: "auto" } });
|
|
22
|
+
const dialogWidth = computed(() => isMobile ? appState.pageWidth.value : 600);
|
|
23
|
+
const dialog = ref(false);
|
|
20
24
|
|
|
21
25
|
start();
|
|
22
26
|
|
|
@@ -28,68 +32,111 @@
|
|
|
28
32
|
}
|
|
29
33
|
async function search() {
|
|
30
34
|
const sel = selected.value;
|
|
31
|
-
if (
|
|
32
|
-
books.value = await booksService.getFullBooks(sel.sN, sel.gN, title.value, flags.value, rating.value);
|
|
33
|
-
else if (content.value)
|
|
35
|
+
if (content.value)
|
|
34
36
|
books.value = await booksService.getFullBooksByContent(content.value, 15);
|
|
37
|
+
else if (sel)
|
|
38
|
+
books.value = await booksService.getFullBooks(sel.sN, sel.gN, title.value, flags.value, rating.value);
|
|
35
39
|
}
|
|
36
40
|
function select (book: IFullBook) {
|
|
37
41
|
selectedBook.value = book;
|
|
38
42
|
booksService.selectedAuthorId = book.authorId;
|
|
39
43
|
booksService.selectedBookId = book.bookId;
|
|
40
44
|
}
|
|
45
|
+
async function showInfo (book: IFullBook) {
|
|
46
|
+
const books = await booksService.getBooks(undefined, book.bookId);
|
|
47
|
+
showBook.value = books ? books[0] : null;
|
|
48
|
+
if (showBook.value) {
|
|
49
|
+
dialog.value = true;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
function onMicrophone(result: SpeechRecognitionResult) {
|
|
53
|
+
content.value = result[0].transcript;
|
|
54
|
+
}
|
|
41
55
|
</script>
|
|
42
56
|
|
|
43
57
|
<template>
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
58
|
+
<v-container fluid class="bg-office" :style="heightStyle">
|
|
59
|
+
<v-defaults-provider :defaults="{'VBtn':{'size':'large','variant':'flat','class':'bg-office'}}">
|
|
60
|
+
<v-row v-if="isMobile" density="compact" class="align-center">
|
|
61
|
+
<v-col cols="6">
|
|
62
|
+
<v-select v-model="selected" :items="authors" label="Author" return-object item-value="id" item-title="sN" hide-details density="compact" single-line></v-select>
|
|
63
|
+
</v-col>
|
|
64
|
+
<v-col cols="4">
|
|
65
|
+
<p>{{selected.gN}}</p>
|
|
66
|
+
</v-col>
|
|
67
|
+
<v-col cols="2">
|
|
68
|
+
<v-btn @click="search" icon="$search"></v-btn>
|
|
69
|
+
</v-col>
|
|
70
|
+
<v-col cols="6">
|
|
71
|
+
<v-text-field label="Titel" v-model="title" hide-details density="compact"></v-text-field>
|
|
72
|
+
</v-col>
|
|
73
|
+
<v-col cols="4">
|
|
74
|
+
<v-select label="Status" v-model="flags" :items="booksService.flags" hide-details density="compact" single-line></v-select>
|
|
75
|
+
</v-col>
|
|
76
|
+
<v-col cols="2">
|
|
77
|
+
<v-rating label="Rating" hover clearable :length="5" :size="16" v-model="rating" active-color="primary" />
|
|
78
|
+
</v-col>
|
|
79
|
+
</v-row>
|
|
80
|
+
<v-row v-else density="compact" class="align-center">
|
|
81
|
+
<v-col cols="2">
|
|
82
|
+
<v-select v-model="selected" :items="authors" label="Author" return-object item-value="id" item-title="sN" hide-details density="compact" clearable></v-select>
|
|
83
|
+
</v-col>
|
|
84
|
+
<v-col cols="2">
|
|
85
|
+
<p>{{selected.gN}}</p>
|
|
86
|
+
</v-col>
|
|
87
|
+
<v-col cols="3">
|
|
88
|
+
<v-text-field label="Titel" v-model="title" hide-details clearable density="compact"></v-text-field>
|
|
89
|
+
</v-col>
|
|
90
|
+
<v-col cols="2">
|
|
91
|
+
<v-select label="Status" v-model="flags" :items="booksService.flags" hide-details density="compact" clearable></v-select>
|
|
92
|
+
</v-col>
|
|
93
|
+
<v-col cols="1">
|
|
94
|
+
<v-rating label="Rating" hover clearable :length="5" :size="32" v-model="rating" active-color="primary" />
|
|
95
|
+
</v-col>
|
|
96
|
+
<v-col cols="2">
|
|
97
|
+
<v-btn @click="search" prepend-icon="$search">Search</v-btn>
|
|
98
|
+
</v-col>
|
|
99
|
+
</v-row>
|
|
100
|
+
<v-row density="compact" class="align-center">
|
|
101
|
+
<v-col cols="11">
|
|
102
|
+
<v-text-field label="Inhalt" v-model="content" hide-details clearable density="compact"></v-text-field>
|
|
103
|
+
</v-col>
|
|
104
|
+
<v-col cols="1">
|
|
105
|
+
<microphone lang="de-AT" cls="aibutton" @result="onMicrophone"></microphone>
|
|
106
|
+
</v-col>
|
|
107
|
+
</v-row>
|
|
108
|
+
<v-divider></v-divider>
|
|
109
|
+
<book-search-line v-for="book in books" :key="book.bookId" :book="book" :selected="selectedBook == book" :ismobile="isMobile" @selectbook="select(book)" @info="showInfo(book)"></book-search-line>
|
|
110
|
+
</v-defaults-provider>
|
|
111
|
+
</v-container>
|
|
112
|
+
<v-dialog v-model="dialog" :fullscreen="isMobile">
|
|
113
|
+
<v-card :width="dialogWidth">
|
|
114
|
+
<v-card-title class="headline">{{showBook!.title}}</v-card-title>
|
|
115
|
+
<v-card-text>
|
|
116
|
+
<v-container>
|
|
117
|
+
<v-row>
|
|
118
|
+
<p>{{showBook!.description}}</p>
|
|
119
|
+
</v-row>
|
|
120
|
+
<v-row>
|
|
121
|
+
<h3 style="height:32px"></h3>
|
|
122
|
+
</v-row>
|
|
123
|
+
<v-row>
|
|
124
|
+
<p>{{showBook!.comment}}</p>
|
|
125
|
+
</v-row>
|
|
126
|
+
</v-container>
|
|
127
|
+
</v-card-text>
|
|
128
|
+
<v-card-actions>
|
|
129
|
+
<v-btn @click="dialog = false">
|
|
130
|
+
<v-icon size="large" icon="$back" />
|
|
131
|
+
</v-btn>
|
|
132
|
+
</v-card-actions>
|
|
133
|
+
</v-card>
|
|
134
|
+
</v-dialog>
|
|
95
135
|
</template>
|
|
136
|
+
|
|
137
|
+
<style scoped>
|
|
138
|
+
.aibutton {
|
|
139
|
+
min-width: 32px;
|
|
140
|
+
padding: 0 8px;
|
|
141
|
+
}
|
|
142
|
+
</style>
|
package/src/views/VideosPage.vue
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
getMediaBinSymbol, getPlayerSymbol, IMediaAppConfig, ITranscodeParams, IPlayerConfiguration
|
|
8
8
|
} from '@christianriedl/media';
|
|
9
9
|
import FileUpload from '../components/FileUpload.vue';
|
|
10
|
+
import Microphone from '@christianriedl/utils/src/components/Microphone.vue';
|
|
10
11
|
|
|
11
12
|
const appState = inject(appStateSymbol)!;
|
|
12
13
|
const appConfig = inject(appConfigSymbol)!;
|
|
@@ -85,14 +86,24 @@
|
|
|
85
86
|
})
|
|
86
87
|
}
|
|
87
88
|
function prepareSearch() {
|
|
88
|
-
actors.value = mediaService.medialists["Video.Actors"].values.sort();
|
|
89
89
|
searchVisible.value = !searchVisible.value;
|
|
90
|
-
if (searchVisible.value && !actors.value.length)
|
|
91
|
-
|
|
90
|
+
if (searchVisible.value && !actors.value.length) {
|
|
91
|
+
const cloneArray = [...mediaService.medialists["Video.Actors"].values];
|
|
92
|
+
cloneArray.sort();
|
|
93
|
+
actors.value = cloneArray;
|
|
94
|
+
}
|
|
92
95
|
}
|
|
93
96
|
async function doSearch() {
|
|
94
97
|
if (content.value) {
|
|
95
|
-
const
|
|
98
|
+
const foundVideos = await mediaService.getVideosByContent(content.value, 20);
|
|
99
|
+
const videos: IVideoFile[] = [];
|
|
100
|
+
for (let i = 0; i < foundVideos.length; i++) { // Use prepared videos from mediaservice folders
|
|
101
|
+
const foundVideo = foundVideos[i];
|
|
102
|
+
const folder = mediaService.folders[foundVideo.dlnaParentId];
|
|
103
|
+
mediaService.prepare(folder);
|
|
104
|
+
const file = folder.files!.find(f => f.dlnaid == foundVideo.dlnaid) as IVideoFile;
|
|
105
|
+
videos.push(file);
|
|
106
|
+
}
|
|
96
107
|
items.splice(0, items.length, ...videos);
|
|
97
108
|
}
|
|
98
109
|
else if (actor.value){
|
|
@@ -285,6 +296,9 @@
|
|
|
285
296
|
alert ("Not found in cloud (OneDrive)");
|
|
286
297
|
}
|
|
287
298
|
}
|
|
299
|
+
function onMicrophone(result: SpeechRecognitionResult) {
|
|
300
|
+
content.value = result[0].transcript;
|
|
301
|
+
}
|
|
288
302
|
</script>
|
|
289
303
|
|
|
290
304
|
<template>
|
|
@@ -346,8 +360,9 @@
|
|
|
346
360
|
<v-card-actions v-if="searchVisible">
|
|
347
361
|
<v-combobox v-model="actor" :items="actors" label="Actors" hide-details density="compact" clearable></v-combobox>
|
|
348
362
|
<v-text-field v-model="content" label="Inhalt" min-width="60%" hide-details density="compact" clearable></v-text-field>
|
|
363
|
+
<microphone lang="de-AT" cls="aibutton" @result="onMicrophone"></microphone>
|
|
349
364
|
<v-btn @click="doSearch">
|
|
350
|
-
|
|
365
|
+
<v-icon size="x-large" icon="$search" />
|
|
351
366
|
</v-btn>
|
|
352
367
|
</v-card-actions>
|
|
353
368
|
</v-card>
|
|
@@ -397,3 +412,10 @@
|
|
|
397
412
|
</v-card>
|
|
398
413
|
</v-dialog>
|
|
399
414
|
</template>
|
|
415
|
+
|
|
416
|
+
<style scoped>
|
|
417
|
+
.aibutton {
|
|
418
|
+
min-width: 32px;
|
|
419
|
+
padding: 0 8px;
|
|
420
|
+
}
|
|
421
|
+
</style>
|