@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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@christianriedl/media",
3
- "version": "1.0.286",
3
+ "version": "1.0.287",
4
4
  "description": "RIC media interfaces",
5
5
 
6
6
  "main": "dist/index.js",
@@ -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="4" >
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="8" >
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 (sel)
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
- <v-container fluid class="bg-office" :style="heightStyle">
45
- <v-defaults-provider :defaults="{'VBtn':{'size':'large','variant':'flat','class':'bg-office'}}">
46
- <v-row v-if="isMobile" density="compact" class="align-center">
47
- <v-col cols="6">
48
- <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>
49
- </v-col>
50
- <v-col cols="4">
51
- <p>{{selected.gN}}</p>
52
- </v-col>
53
- <v-col cols="2">
54
- <v-btn @click="search" icon="$search"></v-btn>
55
- </v-col>
56
- <v-col cols="6">
57
- <v-text-field label="Titel" v-model="title" hide-details density="compact"></v-text-field>
58
- </v-col>
59
- <v-col cols="4">
60
- <v-select label="Status" v-model="flags" :items="booksService.flags" hide-details density="compact" single-line ></v-select>
61
- </v-col>
62
- <v-col cols="2">
63
- <v-rating label="Rating" hover clearable :length="5" :size="16" v-model="rating" active-color="primary" />
64
- </v-col>
65
- </v-row>
66
- <v-row v-else density="compact" class="align-center">
67
- <v-col cols="2">
68
- <v-select v-model="selected" :items="authors" label="Author" return-object item-value="id" item-title="sN" hide-details density="compact" clearable ></v-select>
69
- </v-col>
70
- <v-col cols="2">
71
- <p>{{selected.gN}}</p>
72
- </v-col>
73
- <v-col cols="3">
74
- <v-text-field label="Titel" v-model="title" hide-details clearable density="compact" ></v-text-field>
75
- </v-col>
76
- <v-col cols="2">
77
- <v-select label="Status" v-model="flags" :items="booksService.flags" hide-details density="compact" clearable ></v-select>
78
- </v-col>
79
- <v-col cols="1">
80
- <v-rating label="Rating" hover clearable :length="5" :size="32" v-model="rating" active-color="primary" />
81
- </v-col>
82
- <v-col cols="2">
83
- <v-btn @click="search" prepend-icon="$search">Search</v-btn>
84
- </v-col>
85
- </v-row>
86
- <v-row density="compact" class="align-center">
87
- <v-col cols="12">
88
- <v-text-field label="Inhalt" v-model="content" hide-details clearable density="compact"></v-text-field>
89
- </v-col>
90
- </v-row>
91
- <v-divider></v-divider>
92
- <book-search-line v-for="book in books" :key="book.bookId" :book="book" :selected="selectedBook == book" :ismobile="isMobile" @selectbook="select(book)"></book-search-line>
93
- </v-defaults-provider>
94
- </v-container>
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>
@@ -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
- actors.value = mediaService.medialists["Video.Actors"].values.sort();
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 videos = await mediaService.getVideosByContent(content.value, 100);
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
- <v-icon size="x-large" icon="$search" />
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>