WIP improve export
This commit is contained in:
parent
9a124c5ab9
commit
94bf35875a
3 changed files with 174 additions and 169 deletions
|
|
@ -27,12 +27,12 @@
|
||||||
<div v-if="!event.isRelation() && !event.isRedacted() && !event.isRedaction()" :ref="event.getId()">
|
<div v-if="!event.isRelation() && !event.isRedacted() && !event.isRedaction()" :ref="event.getId()">
|
||||||
<div class="message-wrapper">
|
<div class="message-wrapper">
|
||||||
<component
|
<component
|
||||||
:is="componentForEvent(event, true)"
|
:is="componentForEventForExport(event, true)"
|
||||||
:room="room"
|
:room="room"
|
||||||
:originalEvent="event"
|
:originalEvent="event"
|
||||||
:nextEvent="events[index + 1]"
|
:nextEvent="events[index + 1]"
|
||||||
:timelineSet="timelineSet"
|
:timelineSet="timelineSet"
|
||||||
:componentFn="componentForEvent"
|
:componentFn="componentForEventForExport"
|
||||||
ref="exportedEvent"
|
ref="exportedEvent"
|
||||||
v-on:layout-change="onLayoutChange"
|
v-on:layout-change="onLayoutChange"
|
||||||
/>
|
/>
|
||||||
|
|
@ -153,6 +153,7 @@ export default {
|
||||||
return {
|
return {
|
||||||
timelineSet: null,
|
timelineSet: null,
|
||||||
events: [],
|
events: [],
|
||||||
|
exportComponents: [],
|
||||||
fetchedEvents: 0,
|
fetchedEvents: 0,
|
||||||
totalEvents: 0,
|
totalEvents: 0,
|
||||||
processedEvents: 0,
|
processedEvents: 0,
|
||||||
|
|
@ -179,6 +180,22 @@ export default {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
componentForEventForExport(event, forExport) {
|
||||||
|
const comp = this.componentForEvent(event, forExport);
|
||||||
|
const self = this;
|
||||||
|
if (comp) {
|
||||||
|
if (!comp.mixins) {
|
||||||
|
comp.mixins = [];
|
||||||
|
}
|
||||||
|
comp.mixins.push({
|
||||||
|
created: function() {
|
||||||
|
console.error("Created", this.name);
|
||||||
|
self.exportComponents.push(this);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return comp;
|
||||||
|
},
|
||||||
cancelExport() {
|
cancelExport() {
|
||||||
this.cancelled = true;
|
this.cancelled = true;
|
||||||
},
|
},
|
||||||
|
|
@ -230,6 +247,8 @@ export default {
|
||||||
var currentMediaSize = 0;
|
var currentMediaSize = 0;
|
||||||
var maxMediaSize = 1024 * 1024 * 1024; // 1GB
|
var maxMediaSize = 1024 * 1024 * 1024; // 1GB
|
||||||
|
|
||||||
|
this.exportComponents = [];
|
||||||
|
|
||||||
this.getEvents()
|
this.getEvents()
|
||||||
.then((events) => {
|
.then((events) => {
|
||||||
var decryptionPromises = [];
|
var decryptionPromises = [];
|
||||||
|
|
@ -282,6 +301,8 @@ export default {
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
|
this.totalEvents = this.exportComponents.length;
|
||||||
|
|
||||||
// UI updated, start processing events
|
// UI updated, start processing events
|
||||||
zip = new JSZip();
|
zip = new JSZip();
|
||||||
var avatarFolder = zip.folder("avatars");
|
var avatarFolder = zip.folder("avatars");
|
||||||
|
|
@ -291,186 +312,173 @@ export default {
|
||||||
var filesFolder = zip.folder("files");
|
var filesFolder = zip.folder("files");
|
||||||
|
|
||||||
var downloadPromises = [];
|
var downloadPromises = [];
|
||||||
let components = this.$refs.exportedEvent;
|
|
||||||
for (const parentComp of components) {
|
|
||||||
let childComponents = [parentComp];
|
|
||||||
|
|
||||||
// Some components, i.e. the media threads, have subcomponents
|
for (const comp of this.exportComponents.filter((c) => c.event != undefined)) {
|
||||||
// that we want to export. So pickup subcomponents here as well.
|
// Avatars need downloading?
|
||||||
if (parentComp.$refs && parentComp.$refs.exportedEvent) {
|
if (comp.$el && comp.$el.nodeType == 1) {
|
||||||
if (Array.isArray(parentComp.$refs.exportedEvent)) {
|
const avatars = comp.$el.getElementsByClassName("v-avatar");
|
||||||
for (const child of parentComp.$refs.exportedEvent) {
|
if (avatars && avatars.length > 0) {
|
||||||
childComponents.push(child);
|
const member = this.room.getMember(comp.event.getSender());
|
||||||
}
|
if (member) {
|
||||||
} else {
|
const fileName = comp.event.getSender() + ".png";
|
||||||
childComponents.push(parentComp.$refs.exportedEvent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (const comp of childComponents.filter((c) => c.event != undefined)) {
|
|
||||||
// Avatars need downloading?
|
|
||||||
if (comp.$el && comp.$el.nodeType == 1) {
|
|
||||||
const avatars = comp.$el.getElementsByClassName("v-avatar");
|
|
||||||
if (avatars && avatars.length > 0) {
|
|
||||||
const member = this.room.getMember(comp.event.getSender());
|
|
||||||
if (member) {
|
|
||||||
const fileName = comp.event.getSender() + ".png";
|
|
||||||
|
|
||||||
const setSource = (fileName) => {
|
const setSource = (fileName) => {
|
||||||
for (let avatarIndex = 0; avatarIndex < avatars.length; avatarIndex++) {
|
for (let avatarIndex = 0; avatarIndex < avatars.length; avatarIndex++) {
|
||||||
const avatarElement = avatars[avatarIndex];
|
const avatarElement = avatars[avatarIndex];
|
||||||
const images = avatarElement.getElementsByTagName("img");
|
const images = avatarElement.getElementsByTagName("img");
|
||||||
for (let imageIndex = 0; imageIndex < images.length; imageIndex++) {
|
for (let imageIndex = 0; imageIndex < images.length; imageIndex++) {
|
||||||
const img = images[imageIndex];
|
const img = images[imageIndex];
|
||||||
img.onerror = undefined;
|
img.onerror = undefined;
|
||||||
img.removeAttribute("src");
|
img.removeAttribute("src");
|
||||||
img.setAttribute("data-exported-src", "./avatars/" + fileName);
|
img.setAttribute("data-exported-src", "./avatars/" + fileName);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
if (!avatarFolder.file(fileName)) {
|
|
||||||
const url = member.getAvatarUrl(
|
|
||||||
this.$matrix.matrixClient.getHomeserverUrl(),
|
|
||||||
40,
|
|
||||||
40,
|
|
||||||
"scale",
|
|
||||||
true,
|
|
||||||
false,
|
|
||||||
this.$matrix.useAuthedMedia
|
|
||||||
);
|
|
||||||
if (url) {
|
|
||||||
avatarFolder.file(fileName, "empty");
|
|
||||||
downloadPromises.push(
|
|
||||||
axios
|
|
||||||
.get(url, {
|
|
||||||
responseType: "blob",
|
|
||||||
headers: this.$matrix.useAuthedMedia
|
|
||||||
? {
|
|
||||||
Authorization: `Bearer ${this.$matrix.matrixClient.getAccessToken()}`,
|
|
||||||
}
|
|
||||||
: undefined,
|
|
||||||
})
|
|
||||||
.then((result) => {
|
|
||||||
if (result.data) {
|
|
||||||
avatarFolder.file(fileName, result.data);
|
|
||||||
setSource(fileName);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
console.error("Download error: ", err);
|
|
||||||
avatarFolder.remove(fileName);
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
setSource(fileName);
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!avatarFolder.file(fileName)) {
|
||||||
|
const url = member.getAvatarUrl(
|
||||||
|
this.$matrix.matrixClient.getHomeserverUrl(),
|
||||||
|
40,
|
||||||
|
40,
|
||||||
|
"scale",
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
this.$matrix.useAuthedMedia
|
||||||
|
);
|
||||||
|
if (url) {
|
||||||
|
avatarFolder.file(fileName, "empty");
|
||||||
|
downloadPromises.push(
|
||||||
|
axios
|
||||||
|
.get(url, {
|
||||||
|
responseType: "blob",
|
||||||
|
headers: this.$matrix.useAuthedMedia
|
||||||
|
? {
|
||||||
|
Authorization: `Bearer ${this.$matrix.matrixClient.getAccessToken()}`,
|
||||||
|
}
|
||||||
|
: undefined,
|
||||||
|
})
|
||||||
|
.then((result) => {
|
||||||
|
if (result.data) {
|
||||||
|
avatarFolder.file(fileName, result.data);
|
||||||
|
setSource(fileName);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.error("Download error: ", err);
|
||||||
|
avatarFolder.remove(fileName);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
setSource(fileName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let componentClass = comp.$options
|
let attachment =
|
||||||
? comp.$options.__file.split("/").reverse()[0].split(".")[0]
|
comp.event && comp.event.getId
|
||||||
: "invalid_component";
|
? this.$matrix.attachmentManager.getEventAttachment(comp.event)
|
||||||
let attachment =
|
: undefined;
|
||||||
comp.event && comp.event.getId
|
let componentClass = comp.$options
|
||||||
? this.$matrix.attachmentManager.getEventAttachment(comp.event)
|
? comp.$options.__file.split("/").reverse()[0].split(".")[0]
|
||||||
: undefined;
|
: "invalid_component";
|
||||||
|
console.error("Processi", componentClass, comp.event, comp.originalEvent, attachment);
|
||||||
|
|
||||||
if (attachment && (attachment.srcSize = 0 || currentMediaSize + attachment.srcSize <= maxMediaSize)) {
|
|
||||||
downloadPromises.push(
|
|
||||||
attachment
|
|
||||||
.loadSrc({ asBlob: true })
|
|
||||||
.then((res) => {
|
|
||||||
const blob = res.data;
|
|
||||||
if (currentMediaSize + blob.size <= maxMediaSize) {
|
|
||||||
currentMediaSize += blob.size;
|
|
||||||
|
|
||||||
switch (componentClass) {
|
if (attachment && (attachment.srcSize = 0 || currentMediaSize + attachment.srcSize <= maxMediaSize)) {
|
||||||
case "MessageIncomingImageExport":
|
downloadPromises.push(
|
||||||
case "MessageOutgoingImageExport":
|
attachment
|
||||||
{
|
.loadSrc({ asBlob: true })
|
||||||
let mime = blob.type;
|
.then((res) => {
|
||||||
var extension = ".png";
|
const blob = res.data;
|
||||||
switch (mime) {
|
if (currentMediaSize + blob.size <= maxMediaSize) {
|
||||||
case "image/jpeg":
|
currentMediaSize += blob.size;
|
||||||
case "image/jpg":
|
|
||||||
extension = ".jpg";
|
|
||||||
break;
|
|
||||||
case "image/gif":
|
|
||||||
extension = ".gif";
|
|
||||||
}
|
|
||||||
|
|
||||||
let fileName = comp.event.getId() + extension;
|
switch (componentClass) {
|
||||||
imageFolder.file(fileName, blob); // TODO calc bytes
|
case "MessageIncomingImageExport":
|
||||||
|
case "MessageOutgoingImageExport":
|
||||||
// Update source
|
{
|
||||||
const images = comp.$el.getElementsByTagName("img");
|
let mime = blob.type;
|
||||||
for (let imageIndex = 0; imageIndex < images.length; imageIndex++) {
|
var extension = ".png";
|
||||||
const img = images[imageIndex];
|
switch (mime) {
|
||||||
img.removeAttribute("src");
|
case "image/jpeg":
|
||||||
img.setAttribute("data-exported-src", "./images/" + fileName);
|
case "image/jpg":
|
||||||
}
|
extension = ".jpg";
|
||||||
this.processedEvents += 1;
|
break;
|
||||||
|
case "image/gif":
|
||||||
|
extension = ".gif";
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
|
|
||||||
case "MessageIncomingAudioExport":
|
let fileName = comp.event.getId() + extension;
|
||||||
case "MessageOutgoingAudioExport":
|
imageFolder.file(fileName, blob); // TODO calc bytes
|
||||||
{
|
|
||||||
var extension = ".webm";
|
|
||||||
let fileName = comp.event.getId() + extension;
|
|
||||||
audioFolder.file(fileName, blob); // TODO calc bytes
|
|
||||||
let elements = comp.$el.getElementsByTagName("audio");
|
|
||||||
let element = elements && elements[0];
|
|
||||||
if (element) {
|
|
||||||
element.setAttribute("data-exported-src", "./audio/" + fileName);
|
|
||||||
}
|
|
||||||
this.processedEvents += 1;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "MessageIncomingVideoExport":
|
// Update source
|
||||||
case "MessageOutgoingVideoExport":
|
const images = comp.$el.getElementsByTagName("img");
|
||||||
{
|
for (let imageIndex = 0; imageIndex < images.length; imageIndex++) {
|
||||||
var extension = ".mp4";
|
const img = images[imageIndex];
|
||||||
let fileName = comp.event.getId() + extension;
|
img.removeAttribute("src");
|
||||||
videoFolder.file(fileName, blob); // TODO calc bytes
|
img.setAttribute("data-exported-src", "./images/" + fileName);
|
||||||
// comp.src = "./video/" + fileName;
|
|
||||||
let elements = comp.$el.getElementsByTagName("video");
|
|
||||||
let element = elements && elements[0];
|
|
||||||
if (element) {
|
|
||||||
element.setAttribute("data-exported-src", "./video/" + fileName);
|
|
||||||
}
|
|
||||||
this.processedEvents += 1;
|
|
||||||
}
|
}
|
||||||
break;
|
this.processedEvents += 1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case "MessageIncomingFileExport":
|
case "MessageIncomingAudioExport":
|
||||||
case "MessageOutgoingFileExport":
|
case "MessageOutgoingAudioExport":
|
||||||
{
|
{
|
||||||
var extension = util.getFileExtension(comp.event);
|
var extension = ".webm";
|
||||||
let fileName = comp.event.getId() + extension;
|
let fileName = comp.event.getId() + extension;
|
||||||
filesFolder.file(fileName, blob);
|
audioFolder.file(fileName, blob); // TODO calc bytes
|
||||||
comp.href = "./files/" + fileName;
|
let elements = comp.$el.getElementsByTagName("audio");
|
||||||
this.processedEvents += 1;
|
let element = elements && elements[0];
|
||||||
|
if (element) {
|
||||||
|
element.setAttribute("data-exported-src", "./audio/" + fileName);
|
||||||
}
|
}
|
||||||
break;
|
this.processedEvents += 1;
|
||||||
}
|
}
|
||||||
this.processedEvents += 1;
|
break;
|
||||||
return true;
|
|
||||||
} else {
|
case "MessageIncomingVideoExport":
|
||||||
this.processedEvents += 1;
|
case "MessageOutgoingVideoExport":
|
||||||
return false;
|
{
|
||||||
|
var extension = ".mp4";
|
||||||
|
let fileName = comp.event.getId() + extension;
|
||||||
|
videoFolder.file(fileName, blob); // TODO calc bytes
|
||||||
|
// comp.src = "./video/" + fileName;
|
||||||
|
let elements = comp.$el.getElementsByTagName("video");
|
||||||
|
let element = elements && elements[0];
|
||||||
|
if (element) {
|
||||||
|
element.setAttribute("data-exported-src", "./video/" + fileName);
|
||||||
|
}
|
||||||
|
this.processedEvents += 1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "MessageIncomingFileExport":
|
||||||
|
case "MessageOutgoingFileExport":
|
||||||
|
{
|
||||||
|
var extension = util.getFileExtension(comp.event);
|
||||||
|
let fileName = comp.event.getId() + extension;
|
||||||
|
filesFolder.file(fileName, blob);
|
||||||
|
comp.href = "./files/" + fileName;
|
||||||
|
this.processedEvents += 1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
})
|
|
||||||
.catch((ignoredErr) => {
|
|
||||||
this.processedEvents += 1;
|
this.processedEvents += 1;
|
||||||
})
|
return true;
|
||||||
);
|
} else {
|
||||||
} else {
|
this.processedEvents += 1;
|
||||||
this.processedEvents += 1;
|
return false;
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
.catch((ignoredErr) => {
|
||||||
|
this.processedEvents += 1;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
this.processedEvents += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Promise.all(downloadPromises);
|
return Promise.all(downloadPromises);
|
||||||
|
|
|
||||||
|
|
@ -32,9 +32,6 @@ import { MatrixEvent, MatrixEventEvent } from "matrix-js-sdk";
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const $matrix: any = inject("globalMatrix");
|
const $matrix: any = inject("globalMatrix");
|
||||||
|
|
||||||
type RootType = InstanceType<typeof MessageOutgoing | typeof MessageIncoming>;
|
|
||||||
const rootRef = useTemplateRef<RootType>("root");
|
|
||||||
|
|
||||||
const emits = defineEmits<
|
const emits = defineEmits<
|
||||||
MessageEmits & { (event: "layout-change", value: { element: Element | undefined; action: () => void }): void }
|
MessageEmits & { (event: "layout-change", value: { element: Element | undefined; action: () => void }): void }
|
||||||
>();
|
>();
|
||||||
|
|
@ -44,8 +41,7 @@ const props = defineProps<MessageProps>();
|
||||||
|
|
||||||
const processThread = () => {
|
const processThread = () => {
|
||||||
if (!event.value?.isRedacted()) {
|
if (!event.value?.isRedacted()) {
|
||||||
const el = rootRef.value?.$el;
|
_processThread();
|
||||||
emits("layout-change", { element: el, action: _processThread });
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -149,9 +149,10 @@ export class AttachmentManager {
|
||||||
attachment.loadSrc = (options?: EventAttachmentLoadSrcOptions) => {
|
attachment.loadSrc = (options?: EventAttachmentLoadSrcOptions) => {
|
||||||
if (attachment.src && !options?.asBlob) {
|
if (attachment.src && !options?.asBlob) {
|
||||||
return Promise.resolve({data: attachment.src, type: "src"});
|
return Promise.resolve({data: attachment.src, type: "src"});
|
||||||
} else if (attachment.srcPromise) {
|
} else if (attachment.srcPromise && !options?.asBlob) {
|
||||||
return attachment.srcPromise;
|
return attachment.srcPromise;
|
||||||
}
|
}
|
||||||
|
Implement loadBlob somewhere here!
|
||||||
attachment.srcPromise = this._loadEventAttachmentOrThumbnail(event, false, !!options?.asBlob, (percent) => {
|
attachment.srcPromise = this._loadEventAttachmentOrThumbnail(event, false, !!options?.asBlob, (percent) => {
|
||||||
attachment.srcProgress = percent;
|
attachment.srcProgress = percent;
|
||||||
}).then((res) => {
|
}).then((res) => {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue