@christianriedl/media 1.0.265 → 1.0.267
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.
|
|
3
|
+
"version": "1.0.267",
|
|
4
4
|
"description": "RIC media interfaces",
|
|
5
5
|
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
"author": "Christian Riedl",
|
|
19
19
|
"license": "ISC",
|
|
20
20
|
"dependencies": {
|
|
21
|
-
"@christianriedl/utils": "^1.0.
|
|
21
|
+
"@christianriedl/utils": "^1.0.119",
|
|
22
22
|
"@christianriedl/rest": "^1.0.84",
|
|
23
23
|
"@christianriedl/epg": "^1.0.43"
|
|
24
24
|
},
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { ref, computed, inject } from 'vue';
|
|
3
|
-
import { VTreeview } from 'vuetify/labs/VTreeview';
|
|
4
3
|
import { Dictionary, Helper } from '@christianriedl/utils';
|
|
5
4
|
import { IPictureFile, EOrientation, EPictureFlags, getMediaBinSymbol } from '@christianriedl/media';
|
|
6
5
|
|
package/src/views/BooksPage.vue
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { inject, ref, reactive, onMounted, onUnmounted, computed, StyleValue } from 'vue';
|
|
3
3
|
import { useRouter, useRoute } from 'vue-router';
|
|
4
|
-
import { IAppState, appStateSymbol, getOpenAISymbol, IOpenAIService,
|
|
4
|
+
import { IAppState, appStateSymbol, getOpenAISymbol, IOpenAIService, IResponseResult, ISchema, EScope, EDevice } from '@christianriedl/utils';
|
|
5
5
|
import { BooksService, getBooksSymbol, IAuthorShort, IAuthor, IBook } from '@christianriedl/media';
|
|
6
6
|
import BookLine from '../components/BookLine.vue';
|
|
7
7
|
|
|
@@ -283,7 +283,7 @@
|
|
|
283
283
|
return true;
|
|
284
284
|
}
|
|
285
285
|
async function getAuthorDataFromAI(surName: string, givenName: string) : Promise<boolean> {
|
|
286
|
-
const schema =
|
|
286
|
+
const schema : ISchema =
|
|
287
287
|
{
|
|
288
288
|
"name": "author_data",
|
|
289
289
|
"schema": {
|
|
@@ -302,20 +302,20 @@
|
|
|
302
302
|
},
|
|
303
303
|
"required": ["geburtstag", "sterbetag", "geburtsland"],
|
|
304
304
|
"type": "object"
|
|
305
|
-
}
|
|
306
|
-
"strict": true
|
|
305
|
+
}
|
|
307
306
|
}
|
|
308
307
|
const prompt = `Geburtstag, Sterbetag, Geburtsland von ${givenName} ${surName} ?`;
|
|
309
|
-
const result = await openAI.
|
|
310
|
-
if (result) {
|
|
308
|
+
const result = await openAI.response(prompt, undefined, undefined, schema);
|
|
309
|
+
if (result.json) {
|
|
310
|
+
const json = result.json as any;
|
|
311
311
|
if (!currentAuthor.yearOfBirth)
|
|
312
|
-
currentAuthor.yearOfBirth = getYear(
|
|
312
|
+
currentAuthor.yearOfBirth = getYear(json.geburtstag);
|
|
313
313
|
if (!currentAuthor.yearOfDeath)
|
|
314
|
-
currentAuthor.yearOfDeath =
|
|
314
|
+
currentAuthor.yearOfDeath = json.sterbetag ? getYear(json.sterbetag) : undefined;
|
|
315
315
|
if (!currentAuthor.category)
|
|
316
316
|
currentAuthor.category = booksService.categories[0];
|
|
317
317
|
if (!currentAuthor.country)
|
|
318
|
-
currentAuthor.country =
|
|
318
|
+
currentAuthor.country = json.geburtsland;
|
|
319
319
|
return true;
|
|
320
320
|
}
|
|
321
321
|
else {
|
|
@@ -332,7 +332,7 @@
|
|
|
332
332
|
return 0;
|
|
333
333
|
}
|
|
334
334
|
async function getAuthorBooksFromAI(surName: string, givenName: string, authorId: number) : Promise <IBook[]> {
|
|
335
|
-
const schema =
|
|
335
|
+
const schema: ISchema =
|
|
336
336
|
{
|
|
337
337
|
"name": "books",
|
|
338
338
|
"schema": {
|
|
@@ -358,15 +358,15 @@
|
|
|
358
358
|
},
|
|
359
359
|
"required": ["werke"],
|
|
360
360
|
"type": "object"
|
|
361
|
-
}
|
|
362
|
-
"strict": true
|
|
361
|
+
}
|
|
363
362
|
}
|
|
364
363
|
const prompt = `Alle Buchtitel in Deutsch mit Erscheinungsjahr von ${givenName} ${surName}`;
|
|
365
|
-
const result = await openAI.
|
|
366
|
-
if (result && result.
|
|
364
|
+
const result = await openAI.response(prompt, undefined, undefined, schema);
|
|
365
|
+
if (result && result.json) {
|
|
367
366
|
const books: IBook[] = [];
|
|
368
|
-
|
|
369
|
-
|
|
367
|
+
const json = result.json as any;
|
|
368
|
+
for (let i = 0; i < json.werke.length; i++) {
|
|
369
|
+
books.push ({ id: i, authorId: authorId, title: json.werke[i].werk, year : json.werke[i].erscheinungsjahr});
|
|
370
370
|
}
|
|
371
371
|
return books;
|
|
372
372
|
}
|
|
@@ -377,9 +377,9 @@
|
|
|
377
377
|
}
|
|
378
378
|
async function getBookDescriptionFromAI(surName: string, givenName: string, title: string, prefix?: string) : Promise <string> {
|
|
379
379
|
const prompt = `Eine ${prefix} Inhaltsangabe von dem Buch "${title}" von ${givenName} ${surName}`;
|
|
380
|
-
const result = await openAI.
|
|
381
|
-
if (result && result.
|
|
382
|
-
return result.
|
|
380
|
+
const result = await openAI.response(prompt);
|
|
381
|
+
if (result && result.text) {
|
|
382
|
+
return result.text;
|
|
383
383
|
}
|
|
384
384
|
else {
|
|
385
385
|
alert("No AI result for : " + prompt);
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import { inject, ref, reactive,
|
|
2
|
+
import { inject, ref, reactive, computed } from 'vue';
|
|
3
3
|
import { useRouter, useRoute } from 'vue-router';
|
|
4
|
-
import { IAppState, appStateSymbol,
|
|
5
|
-
import { BooksService, getBooksSymbol,
|
|
4
|
+
import { IAppState, appStateSymbol, EScope, EDevice } from '@christianriedl/utils';
|
|
5
|
+
import { BooksService, getBooksSymbol, IFullBook, IPlace } from '@christianriedl/media';
|
|
6
6
|
import BookPlace from '../components/BookPlace.vue';
|
|
7
7
|
|
|
8
8
|
const route = useRoute();
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import { inject, ref, reactive,
|
|
3
|
-
import { IAppState, appStateSymbol,
|
|
2
|
+
import { inject, ref, reactive, computed, StyleValue } from 'vue';
|
|
3
|
+
import { IAppState, appStateSymbol, EDevice } from '@christianriedl/utils';
|
|
4
4
|
import { BooksService, getBooksSymbol, IAuthorShort, IAuthor, IFullBook } from '@christianriedl/media';
|
|
5
5
|
import BookSearchLine from '../components/BookSearchLine.vue';
|
|
6
6
|
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { inject, ref, reactive, watch, computed, onMounted, StyleValue } from 'vue';
|
|
3
3
|
import { useRouter, useRoute } from 'vue-router';
|
|
4
4
|
import { IAppState, appStateSymbol, appConfigSymbol, EScope, Helper } from '@christianriedl/utils';
|
|
5
|
+
import { getOpenAISymbol, IOpenAIServiceWithVectorStore } from '@christianriedl/utils';
|
|
5
6
|
import { getMediaBinSymbol, getMediaInstanceSymbol, IDocumentInfo, IMediaAppConfig } from '@christianriedl/media';
|
|
6
7
|
import FileUpload from '../components/FileUpload.vue';
|
|
7
8
|
|
|
@@ -9,6 +10,8 @@
|
|
|
9
10
|
const router = useRouter();
|
|
10
11
|
const appState = inject(appStateSymbol)!;
|
|
11
12
|
const mediaAppConfig = inject(appConfigSymbol)! as unknown as IMediaAppConfig;
|
|
13
|
+
const getAiService = inject(getOpenAISymbol)!;
|
|
14
|
+
const aiService = getAiService() as IOpenAIServiceWithVectorStore;
|
|
12
15
|
const officeExtensions: string[] = mediaAppConfig.officeExtensions.split(',');
|
|
13
16
|
const isMobile = appState.isMobile;
|
|
14
17
|
const getMediaService = inject(getMediaBinSymbol)!;
|
|
@@ -27,86 +30,94 @@
|
|
|
27
30
|
const directoryInfos = reactive<IDocumentInfo[]>([]);
|
|
28
31
|
const fileInfos = reactive<IDocumentInfo[]>([]);
|
|
29
32
|
const heightStyle = computed<StyleValue>(() => { return { height: appState.bodyHeight.value + 'px', overflowY: 'auto' } });
|
|
33
|
+
const inVectorStore = reactive<Dictionary<string>>({});
|
|
30
34
|
|
|
31
35
|
onMounted(() => {
|
|
32
|
-
|
|
33
|
-
|
|
36
|
+
mediaService.log.trace('Documents created');
|
|
37
|
+
start();
|
|
34
38
|
});
|
|
35
39
|
async function start() {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
40
|
+
if (route.query && route.query.path) {
|
|
41
|
+
path.value = decodeURIComponent(route.query.path.toString());
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
path.value = "";
|
|
45
|
+
}
|
|
46
|
+
aiService.initializeVectorStore()
|
|
47
|
+
.then(() => {
|
|
48
|
+
for (const key in aiService.vectorStore) {
|
|
49
|
+
const store = aiService.vectorStore[key];
|
|
50
|
+
for (const file in store.files) {
|
|
51
|
+
inVectorStore[store.files[file]] = key;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
getItems(path.value);
|
|
43
56
|
}
|
|
44
57
|
watch(() => route.query, () => start());
|
|
45
58
|
|
|
46
59
|
async function getItems(fullPath: string) {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
60
|
+
if (isMobile) {
|
|
61
|
+
const directories = await mediaService.getDirectories(fullPath);
|
|
62
|
+
directoryNames.splice(0, directoryNames.length, ...directories);
|
|
63
|
+
const files = await mediaService.getFiles(fullPath);
|
|
64
|
+
fileNames.splice(0, fileNames.length, ...files);
|
|
65
|
+
mediaService.log.trace(`At ${fullPath} : ${directories.length} directories, ${files.length} files`);
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
const directories = await mediaService.getDirectoriesFull(fullPath);
|
|
69
|
+
directoryInfos.splice(0, directoryInfos.length, ...directories);
|
|
70
|
+
const files = await mediaService.getFilesFull(fullPath);
|
|
71
|
+
fileInfos.splice(0, fileInfos.length, ...files);
|
|
72
|
+
mediaService.log.trace(`At ${fullPath} : ${directories.length} directories, ${files.length} files`);
|
|
73
|
+
}
|
|
74
|
+
backVisible.value = path.value.length > 0;
|
|
62
75
|
}
|
|
63
76
|
async function onSelect(name: string) {
|
|
64
|
-
|
|
65
|
-
|
|
77
|
+
const fullPath = path.value.length > 0 ? (path.value + '/' + name) : name;
|
|
78
|
+
router.push({ path: 'documents', query: { path: encodeURIComponent(fullPath) } });
|
|
66
79
|
}
|
|
67
80
|
async function onSearch() {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
81
|
+
const files = await mediaService.searchFiles(path.value, contains.value, like.value, recursive.value);
|
|
82
|
+
directoryInfos.splice(0, directoryInfos.length);
|
|
83
|
+
fileInfos.splice(0, fileInfos.length, ...files);
|
|
84
|
+
mediaService.log.trace(`At ${path.value} : search ${files.length} files`);
|
|
72
85
|
}
|
|
73
86
|
function download(name: string, folder?: string) {
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
}
|
|
84
|
-
window.open(url);
|
|
87
|
+
cccccctefcfkhjggitufgrlhcebiccnlcctfrkghrkrd
|
|
88
|
+
const i = name.lastIndexOf('.');
|
|
89
|
+
if (i > 0) {
|
|
90
|
+
const ext = name.substring(i + 1);
|
|
91
|
+
if (officeExtensions.includes(ext)) {
|
|
92
|
+
url = 'https://view.officeapps.live.com/op/view.aspx?src=' + url;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
window.open(url);
|
|
85
96
|
}
|
|
86
97
|
async function listBack() {
|
|
87
|
-
|
|
98
|
+
router.back();
|
|
88
99
|
}
|
|
89
|
-
async function shareDir
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
100
|
+
async function shareDir(name: string) {
|
|
101
|
+
const fullPath = path.value.length > 0 ? (path.value + '/' + name) : name;
|
|
102
|
+
if (!await checkPublic(fullPath)) {
|
|
103
|
+
window.alert("Not a public folder !!");
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
const url = `https://www.christian-riedl.com/photos/documents?path=${encodeURIComponent(fullPath)}`;
|
|
107
|
+
await window.navigator.clipboard.writeText(url);
|
|
108
|
+
window.alert(`Created and Pasted : ${url} !`);
|
|
98
109
|
}
|
|
99
110
|
async function shareFile(name: string, folder?: string) {
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
111
|
+
if (!folder)
|
|
112
|
+
folder = path.value;
|
|
113
|
+
const fullPath = folder.length > 0 ? (folder + '/' + name) : name;
|
|
114
|
+
if (!await checkPublic(fullPath)) {
|
|
115
|
+
window.alert("Not a public folder !!");
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
const url = mediaService.getFileUrl(fullPath);;
|
|
119
|
+
await window.navigator.clipboard.writeText(url);
|
|
120
|
+
window.alert(`Created and Pasted : ${url} !`);
|
|
110
121
|
}
|
|
111
122
|
async function deleteDirOrFile(name: string, folder?: string) {
|
|
112
123
|
if (!folder)
|
|
@@ -127,29 +138,60 @@
|
|
|
127
138
|
function uploadDone() {
|
|
128
139
|
window.location.reload();
|
|
129
140
|
}
|
|
130
|
-
async function checkPublic
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
141
|
+
async function checkPublic(path: string): Promise<boolean> {
|
|
142
|
+
const instanceInfo = await instanceService.getInformation();
|
|
143
|
+
if (instanceInfo != null && instanceInfo.publicDocumentRoots) {
|
|
144
|
+
const publicRoots = instanceInfo.publicDocumentRoots.split(';');
|
|
145
|
+
for (let i = 0; i < publicRoots.length; i++) {
|
|
146
|
+
if (path.startsWith(publicRoots[i]))
|
|
147
|
+
return true;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
return false;
|
|
151
|
+
}
|
|
152
|
+
function vectorColor(file: string): string | undefined {
|
|
153
|
+
if (inVectorStore[file]) {
|
|
154
|
+
return 'warning';
|
|
155
|
+
}
|
|
156
|
+
return undefined;
|
|
157
|
+
}
|
|
158
|
+
async function vectorStore(file: string) {
|
|
159
|
+
if (inVectorStore[file]) {
|
|
160
|
+
if (window.confirm(`File is in vectorstore ${inVectorStore[file]}, delete ?`)) {
|
|
161
|
+
const store = aiService.vectorStore[inVectorStore[file]]
|
|
162
|
+
for (const vsf in store.files) {
|
|
163
|
+
if (store.files[vsf] == file) {
|
|
164
|
+
if (await aiService.deleteFile(vsf))
|
|
165
|
+
delete inVectorStore[file];
|
|
166
|
+
break;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
else {
|
|
172
|
+
const name = window.prompt(`Enter vectorstore name for ${file} to add it`);
|
|
173
|
+
if (name != null) {
|
|
174
|
+
if (!folder)
|
|
175
|
+
folder = path.value;
|
|
176
|
+
let url = mediaService.getFileUrl(folder.length > 0 ? (folder + '/' + name) : name);
|
|
177
|
+
if (await aiService.addFile(url, name) {
|
|
178
|
+
inVectorStore[file] = name;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
140
182
|
}
|
|
141
|
-
function info
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
183
|
+
function info(item: IDocumentInfo): string {
|
|
184
|
+
let sizeStr = "";
|
|
185
|
+
if (item.size) {
|
|
186
|
+
if (item.size < 10000)
|
|
187
|
+
sizeStr = ' ' + item.size.toString() + ' B';
|
|
188
|
+
else if (item.size < 10000000)
|
|
189
|
+
sizeStr = ' ' + Helper.round(item.size / 1000, 2) + ' Kb';
|
|
190
|
+
else
|
|
191
|
+
sizeStr = ' ' + Helper.round(item.size / 1000000, 2) + ' Mb';
|
|
192
|
+
}
|
|
193
|
+
const str = ` - (${Helper.formatDate(item.date)}${sizeStr})`;
|
|
194
|
+
return str;
|
|
153
195
|
}
|
|
154
196
|
</script>
|
|
155
197
|
<template>
|
|
@@ -162,12 +204,13 @@
|
|
|
162
204
|
<v-col cols="1"><v-btn v-if="isAdmin" icon="$delete" class="bg-office" density="compact" variant="flat" @click.stop="deleteDirOrFile(dir)"></v-btn></v-col>
|
|
163
205
|
<v-divider />
|
|
164
206
|
</v-row>
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
207
|
+
<v-row v-for="file in fileNames" :key="file" class="text-decoration-underline text-primary">
|
|
208
|
+
<v-col cols="9"><p class="font-weight-medium text-decoration-underline text-primary" @click.stop="download(file)">{{file}}</p></v-col>
|
|
209
|
+
<v-col cols="1"><v-btn v-if="isAdmin" icon="$brain" class="bg-office" density="compact" variant="flat" :color="vectorColor(file)" @click.stop="vectorStore(file)"></v-btn></v-col>
|
|
210
|
+
<v-col cols="1"><v-btn v-if="isAdmin" icon="$share" class="bg-office" density="compact" variant="flat" @click.stop="shareFile(file)"></v-btn></v-col>
|
|
211
|
+
<v-col cols="1"><v-btn v-if="isAdmin" icon="$delete" class="bg-office" density="compact" variant="flat" @click.stop="deleteDirOrFile(file)"></v-btn></v-col>
|
|
212
|
+
<v-divider />
|
|
213
|
+
</v-row>
|
|
171
214
|
</v-container>
|
|
172
215
|
<v-container v-else fluid>
|
|
173
216
|
<v-row dense align="center">
|