diff --git a/src/app/pages/publications/new-publication/new-publication.page.html b/src/app/pages/publications/new-publication/new-publication.page.html index fe0e05983..0b29f2dfb 100644 --- a/src/app/pages/publications/new-publication/new-publication.page.html +++ b/src/app/pages/publications/new-publication/new-publication.page.html @@ -40,19 +40,23 @@ - + -

mais {{ seletedContent.length - displayLimit }}

diff --git a/src/app/pages/publications/new-publication/new-publication.page.ts b/src/app/pages/publications/new-publication/new-publication.page.ts index 620b320ca..81c4e2ad2 100644 --- a/src/app/pages/publications/new-publication/new-publication.page.ts +++ b/src/app/pages/publications/new-publication/new-publication.page.ts @@ -31,6 +31,7 @@ import { File } from '@ionic-native/file/ngx'; import { Media } from '@ionic-native/media/ngx'; import { checkFileTypeService } from 'src/app/services/checkFileType.service'; import { FileValidatorService } from "src/app/services/file/file-validator.service" +import { PublicationAttachmentEntity } from 'src/app/shared/publication/upload/upload-streaming.service'; const config = { quality: 0.5, maxWidth: 800, @@ -92,6 +93,13 @@ export class NewPublicationPage implements OnInit { publicationTitle: string; imgUrl: any; + + ActionType = { + newRapid : "1", + new: "2", + edit: "3" + } + Defaultimage: any = ''; photo: SafeResourceUrl; @@ -112,7 +120,7 @@ export class NewPublicationPage implements OnInit { photoOrVideo: boolean = false; fileType = ""; filecontent: boolean; - seletedContent: any[] = [] + seletedContent: PublicationAttachmentEntity[] = [] // Set a limit for the number of images to display displayLimit = 4; filesSizeSum = 0; @@ -142,6 +150,18 @@ export class NewPublicationPage implements OnInit { if (this.publication) { this.seletedContent = this.publication.Files; this.filecontent = true; + + this.seletedContent = this.publication.Files.map(e => { + return new PublicationAttachmentEntity( + { + base64: e.FileBase64, + extension: e.FileExtension, + OriginalFileName: e.OriginalFileName, + FileType: this.checkFileType.checkFileType(e.FileExtension) as any + } + ) + }) + } console.log('Edit', this.publication) this.publicationTitle = 'Nova Publicação'; @@ -161,12 +181,16 @@ export class NewPublicationPage implements OnInit { recursive: true }); - document.addEventListener("click", clickOutside, false); - function clickOutside(e) { - const inside = document.getElementById('container-multiselect').contains(e.target); - this.photoOrVideo = false; - console.log(this.photoOrVideo) - } + try { + document.addEventListener("click", clickOutside, false); + function clickOutside(e) { + const inside = document.getElementById('container-multiselect').contains(e.target); + this.photoOrVideo = false; + console.log(this.photoOrVideo) + } + } catch (error) {} + + } // in use @@ -193,11 +217,14 @@ export class NewPublicationPage implements OnInit { console.log('take picture', this.removeTextBeforeSlash(picture, ','),) this.filecontent = true; this.photoOrVideo = false; - let fileObject = { - FileBase64: this.removeTextBeforeSlash(picture, ','), - FileExtension: capturedImage.format, + + const fileObject = new PublicationAttachmentEntity({ + base64: this.removeTextBeforeSlash(picture, ','), + extension: capturedImage.format, + FileType: 'image', OriginalFileName: 'image' - } + }) + this.seletedContent.push(fileObject) @@ -251,11 +278,14 @@ export class NewPublicationPage implements OnInit { .then(async (content) => { this.filecontent = true; - let fileObject = { - FileBase64: 'data:video/mp4;base64,'+content.data, - FileExtension: 'mp4', - OriginalFileName: 'video' - } + let fileObject = new PublicationAttachmentEntity({ + base64: 'data:video/mp4;base64,'+content.data, + extension: 'mp4', + OriginalFileName: 'record', + FileType: 'video' + + }) + this.seletedContent.push(fileObject) }) .catch((err) => console.error(err)); @@ -294,19 +324,30 @@ export class NewPublicationPage implements OnInit { this.filecontent = true; let fileObject; if(this.removeTextBeforeSlash(element.mimeType, '/') == "mp4") { - fileObject = { - FileBase64: 'data:video/mp4;base64,'+ content.data, - FileExtension: this.removeTextBeforeSlash(element.mimeType, '/'), - OriginalFileName: 'video' - } + + const extension = this.removeTextBeforeSlash(element.mimeType, '/') + + fileObject = new PublicationAttachmentEntity({ + base64: content.data, + extension: extension, + OriginalFileName: 'video', + FileType: 'video' + }) + } else { - fileObject = { - FileBase64: content.data, - FileExtension: this.removeTextBeforeSlash(element.mimeType, '/'), - OriginalFileName: 'image' - } + + const extension = this.removeTextBeforeSlash(element.mimeType, '/') + + fileObject = new PublicationAttachmentEntity({ + base64: content.data, + extension: extension, + OriginalFileName: 'image', + FileType: 'image' + + }) + } - + this.seletedContent.push(fileObject) }) .catch((err) => console.error(err)); @@ -353,24 +394,24 @@ export class NewPublicationPage implements OnInit { /* // in use async laodPicture() { - + const capturedImage = await Camera.getPhoto({ quality: 90, // allowEditing: true, resultType: CameraResultType.Uri, source: CameraSource.Photos }); - + const response = await fetch(capturedImage.webPath!); const blob = await response.blob(); - + this.convertBlobToBase64Worker.postMessage(blob); this.convertBlobToBase64Worker.onmessage = async (oEvent)=> { this.capturedImage = oEvent.data this.capturedImageTitle = 'foto' - + } - + } */ @@ -497,7 +538,7 @@ export class NewPublicationPage implements OnInit { OriginalFileName: "video", } } - + return e }) */ @@ -533,8 +574,6 @@ export class NewPublicationPage implements OnInit { try { - - await this.publications.CreatePublication(this.folderId, this.publication).toPromise(); this.close(); this.httpErrorHandle.httpsSucessMessagge('Criar publicação') @@ -740,12 +779,16 @@ export class NewPublicationPage implements OnInit { if (this.checkFileType.checkFileType(FileExtension) == 'image' || this.checkFileType.checkFileType(FileExtension) == 'video') { let resultUrl = decodeURIComponent(element.url); Filesystem.readFile({ path: resultUrl }).then(async (content) => { - console.log('shared base', content.data) - let fileObject = { - FileBase64: this.removeTextBeforeSlash(content.data, ','), - FileExtension: FileExtension, - OriginalFileName: 'shared', - } + + let fileObject = new PublicationAttachmentEntity( + { + base64: this.removeTextBeforeSlash(content.data, ','), + extension: FileExtension, + OriginalFileName: "share-content", + FileType: this.checkFileType.checkFileType(FileExtension) as any + } + ) + this.seletedContent.push(fileObject) }) } else { diff --git a/src/app/pages/publications/publications.page.ts b/src/app/pages/publications/publications.page.ts index 5692a8d4e..33a8e9d02 100644 --- a/src/app/pages/publications/publications.page.ts +++ b/src/app/pages/publications/publications.page.ts @@ -98,6 +98,7 @@ export class PublicationsPage implements OnInit { this.hideRefreshButton(); this.intent = window["sharedContent"] + window["refreshPublication"] = this.refreshing } @@ -124,7 +125,7 @@ export class PublicationsPage implements OnInit { } } - refreshing() { + refreshing = () => { setTimeout(() => { this.getActions(); }, 1500); diff --git a/src/app/services/socket-connection-mcr.service.ts b/src/app/services/socket-connection-mcr.service.ts index 2028c3b52..9b6374b81 100644 --- a/src/app/services/socket-connection-mcr.service.ts +++ b/src/app/services/socket-connection-mcr.service.ts @@ -10,60 +10,60 @@ import { HubConnectionBuilder } from '@microsoft/signalr'; providedIn: 'root' }) export class SocketConnectionMCRService { - private callbacks: Function[] = [] - private onDisconnect: Function[] = [] - private onConnect: Function[] = [] + // private callbacks: Function[] = [] + // private onDisconnect: Function[] = [] + // private onConnect: Function[] = [] - constructor(private http: HttpClient,) { - window["http"] = this.http - } + // constructor(private http: HttpClient,) { + // window["http"] = this.http + // } - connect() { + // connect() { - var connection = new signalR.HubConnectionBuilder() - .withUrl("https://gdcmapi-dev.dyndns.info/FileHub", { - accessTokenFactory: () => "Bearer "+SessionStore.user.Authorization - }).configureLogging(signalR.LogLevel.Information) - .build(); + // var connection = new signalR.HubConnectionBuilder() + // .withUrl("https://gdcmapi-dev.dyndns.info/FileHub", { + // accessTokenFactory: () => "Bearer "+SessionStore.user.Authorization + // }).configureLogging(signalR.LogLevel.Information) + // .build(); - connection.on("ReceiveMessage", (message) => { - console.log("ReceiveMessage", message) - }) + // connection.on("ReceiveMessage", (message) => { + // console.log("ReceiveMessage", message) + // }) - connection.onreconnected((connectionId) => { - console.assert(connection.state === signalR.HubConnectionState.Connected); - console.log(`Reconnected with connectionId: ${connectionId}`); - }); + // connection.onreconnected((connectionId) => { + // console.assert(connection.state === signalR.HubConnectionState.Connected); + // console.log(`Reconnected with connectionId: ${connectionId}`); + // }); - connection.start() - .then(() => { - console.log("SignalR connection started."); - }) - .catch((error) => { - console.error("Error starting SignalR connection:", error); - }); + // connection.start() + // .then(() => { + // console.log("SignalR connection started."); + // }) + // .catch((error) => { + // console.error("Error starting SignalR connection:", error); + // }); - connection.onclose((error) => { - connection.start() - console.log("SignalR connection closed:", error); - }); + // connection.onclose((error) => { + // connection.start() + // console.log("SignalR connection closed:", error); + // }); - } + // } - subscribe(callback) { - this.callbacks.push(callback); - } + // subscribe(callback) { + // this.callbacks.push(callback); + // } - unsubscribe(callback) { - this.callbacks = this.callbacks.filter(cb => cb !== callback); - } + // unsubscribe(callback) { + // this.callbacks = this.callbacks.filter(cb => cb !== callback); + // } - onDisconnectCallback(callback) { - this.onDisconnect.push(callback) - } - onConnectCallback(callback) { - this.onConnect.push(callback) - } + // onDisconnectCallback(callback) { + // this.onDisconnect.push(callback) + // } + // onConnectCallback(callback) { + // this.onConnect.push(callback) + // } } @@ -74,6 +74,7 @@ class ReconnectingWebSocketSignalR { private callbacks: Function[] = [] private onDisconnect: Function[] = [] private onConnect: Function[] = [] + private stop = true constructor() { this.isOpen = false; @@ -81,9 +82,13 @@ class ReconnectingWebSocketSignalR { } connect() { + console.log("try to connect=================================") + this.stop = false + this.connection = new signalR.HubConnectionBuilder() .withUrl("https://gdcmapi-dev.dyndns.info/FileHub", { - accessTokenFactory: () => "Bearer "+SessionStore.user.Authorization + transport: signalR.HttpTransportType.LongPolling, + accessTokenFactory: () => SessionStore.user.Authorization }).configureLogging(signalR.LogLevel.Information) .build(); @@ -95,16 +100,15 @@ class ReconnectingWebSocketSignalR { this.onConnect.forEach(callback => callback()); - console.log("SignalR connection started."); }) .catch((error) => { - console.error("Error starting SignalR connection:", error); + console.error("Error starting SignalR connection:", error); }); this.connection.on("ReceiveMessage", (message) => { const data: any = JSON.parse(message) - console.log(data) + console.log("ReceiveMessage", data) this.callbacks.forEach(callback => callback(data)); }) @@ -113,12 +117,27 @@ class ReconnectingWebSocketSignalR { this.isOpen = false; this.onDisconnect.forEach(callback => callback()); // Attempt to reconnect after a delay - setTimeout(() => { - this.connect(); - }, 1000); // Adjust the delay as needed + if(this.stop) { + setTimeout(() => { + this.connect(); + }, 1000); // Adjust the delay as needed + } + }); + } - + disconnect() { + this.stop = true + this.connection.stop() + .then(() => { + console.log('WebSocket connection closed'); + this.isOpen = false; + this.onDisconnect.forEach(callback => callback()); + console.log("SignalR connection stopped."); + }) + .catch((error) => { + console.error("Error stopping SignalR connection:", error); + }); } subscribe(callback) { @@ -143,81 +162,81 @@ interface socketResponse { Guid: string isCompleted: Boolean } -class ReconnectingWebSocket { +// class ReconnectingWebSocket { - private url: string - private socket - isOpen: boolean - private callbacks: Function[] = [] - private onDisconnect: Function[] = [] - private onConnect: Function[] = [] +// private url: string +// private socket +// isOpen: boolean +// private callbacks: Function[] = [] +// private onDisconnect: Function[] = [] +// private onConnect: Function[] = [] - http: HttpClient = window["http"] +// http: HttpClient = window["http"] - constructor(url) { - this.url = url; - this.socket = null; - this.isOpen = false; - this.connect(); - } +// constructor(url) { +// this.url = url; +// this.socket = null; +// this.isOpen = false; +// this.connect(); +// } - connect() { - this.socket = new WebSocket(this.url); +// connect() { +// this.socket = new WebSocket(this.url); - this.socket.addEventListener('open', (event) => { - this.isOpen = true; - console.log('WebSocket connection established'); +// this.socket.addEventListener('open', (event) => { +// this.isOpen = true; +// console.log('WebSocket connection established'); - // Example: Send a message to the server - this.socket.send('Hello, WebSocket Server!'); - this.onConnect.forEach(callback => callback()); - }); +// // Example: Send a message to the server +// this.socket.send('Hello, WebSocket Server!'); +// this.onConnect.forEach(callback => callback()); +// }); - this.socket.addEventListener('message', (event) => { - const data: socketResponse = JSON.parse(event.data) - this.callbacks.forEach(callback => callback(data)); - }); +// this.socket.addEventListener('message', (event) => { +// const data: socketResponse = JSON.parse(event.data) +// this.callbacks.forEach(callback => callback(data)); +// }); - this.socket.addEventListener('close', (event) => { - console.log('WebSocket connection closed'); - this.isOpen = false; - this.onDisconnect.forEach(callback => callback()); - // Attempt to reconnect after a delay - setTimeout(() => { - this.connect(); - }, 1000); // Adjust the delay as needed - }); - } +// this.socket.addEventListener('close', (event) => { +// console.log('WebSocket connection closed'); +// this.isOpen = false; +// this.onDisconnect.forEach(callback => callback()); +// // Attempt to reconnect after a delay +// setTimeout(() => { +// this.connect(); +// }, 1000); // Adjust the delay as needed +// }); +// } - send(message) { - if (this.isOpen) { - this.socket.send(message); - } else { - console.error('WebSocket connection is not open. Unable to send message.'); - } - } +// send(message) { +// if (this.isOpen) { +// this.socket.send(message); +// } else { +// console.error('WebSocket connection is not open. Unable to send message.'); +// } +// } - close() { - if (this.isOpen) { - this.socket.close(); - } - } +// close() { +// if (this.isOpen) { +// this.socket.close(); +// } +// } - subscribe(callback) { - this.callbacks.push(callback); - } +// subscribe(callback) { +// this.callbacks.push(callback); +// } - unsubscribe(callback) { - this.callbacks = this.callbacks.filter(cb => cb !== callback); - } +// unsubscribe(callback) { +// this.callbacks = this.callbacks.filter(cb => cb !== callback); +// } - onDisconnectCallback(callback) { - this.onDisconnect.push(callback) - } - onConnectCallback(callback) { - this.onConnect.push(callback) - } -} +// onDisconnectCallback(callback) { +// this.onDisconnect.push(callback) +// } +// onConnectCallback(callback) { +// this.onConnect.push(callback) +// } +// } // export class ObjectMergeNotification{ @@ -293,9 +312,11 @@ export class ObjectMergeNotification{ this.socket.onConnectCallback(()=> { this.runWatch = false }) + this.socket.subscribe((data: socketResponse) => { if(data.isCompleted == true) { + console.log("==================!!!====================") this.callbacks[data.Guid](data) delete this.callbacks[data.Guid] } @@ -305,36 +326,9 @@ export class ObjectMergeNotification{ } - connect() { - - var connection = new signalR.HubConnectionBuilder() - .withUrl("https://gdcmapi-dev.dyndns.info/FileHub", { - accessTokenFactory: () => "Bearer "+SessionStore.user.Authorization - }).configureLogging(signalR.LogLevel.Information) - .build(); - - connection.on("ReceiveMessage", (message) => { - console.log("ReceiveMessage", message) - }) - - connection.onreconnected((connectionId) => { - console.assert(connection.state === signalR.HubConnectionState.Connected); - console.log(`Reconnected with connectionId: ${connectionId}`); - }); - - connection.start() - .then(() => { - console.log("SignalR connection started."); - }) - .catch((error) => { - console.error("Error starting SignalR connection:", error); - }); - - connection.onclose((error) => { - connection.start() - console.log("SignalR connection closed:", error); - }); - + close() { + console.log("close=============================================") + this.socket.disconnect(); } async watch() { @@ -354,6 +348,8 @@ export class ObjectMergeNotification{ }, 1000) + } else { + console.log("end loop============================") } } diff --git a/src/app/shared/API/CMAPI/cmapi-api.service.ts b/src/app/shared/API/CMAPI/cmapi-api.service.ts index 927de3936..ae13fb503 100644 --- a/src/app/shared/API/CMAPI/cmapi-api.service.ts +++ b/src/app/shared/API/CMAPI/cmapi-api.service.ts @@ -14,7 +14,7 @@ export class CMAPIAPIService { getVideoHeader(url: string) { - // return this.http.head('http://localhost:3001/static/'+url, { observe: 'response' }) + //return this.http.head('http://localhost:3001/static/'+url, { observe: 'response' }) return this.http.head(environment.apiURL+'ObjectServer/StreamFiles?path='+url, { observe: 'response' }) } } diff --git a/src/app/shared/publication/new-publication/new-publication.page.html b/src/app/shared/publication/new-publication/new-publication.page.html index 40b765508..4c4b9a21a 100644 --- a/src/app/shared/publication/new-publication/new-publication.page.html +++ b/src/app/shared/publication/new-publication/new-publication.page.html @@ -56,34 +56,24 @@ - - - -
- - -
- @@ -93,7 +83,7 @@
-

mais {{ seletedContent.length - displayLimit }}

diff --git a/src/app/shared/publication/new-publication/new-publication.page.ts b/src/app/shared/publication/new-publication/new-publication.page.ts index d62a5523e..54cbb977e 100644 --- a/src/app/shared/publication/new-publication/new-publication.page.ts +++ b/src/app/shared/publication/new-publication/new-publication.page.ts @@ -22,6 +22,7 @@ import { CaptureError, CaptureImageOptions, MediaCapture, MediaFile } from '@awe import { Filesystem, Directory, Encoding, FilesystemDirectory } from '@capacitor/filesystem'; import { Platform } from '@ionic/angular'; import { Capacitor } from '@capacitor/core'; +import { PublicationAttachmentEntity, PublicationFormMV } from '../upload/upload-streaming.service'; enum ActionType { @@ -70,9 +71,9 @@ export class NewPublicationPage implements OnInit { photoOrVideo: boolean = false; video: any; - publicationFormMV = new PublicationFormMV() + constructor( public photoService: PhotoService, private publications: PublicationsService, @@ -97,7 +98,6 @@ export class NewPublicationPage implements OnInit { this.filecontent = true; } - this.SocketConnectionMCRService.connect() } ngOnInit() { @@ -148,13 +148,15 @@ export class NewPublicationPage implements OnInit { } console.log('edit', this.publication) this.pub = this.publication; + + console.log("this.publication.Files", this.publication.Files) this.seletedContent = this.publication.Files.map(e => { return new PublicationAttachmentEntity( { base64: e.FileBase64, extension: e.FileExtension, OriginalFileName: e.OriginalFileName, - FileType: this.checkFileType.checkFileType(e.FileExtension) + FileType: this.checkFileType.checkFileType(e.FileExtension) as any } ) }) @@ -186,11 +188,6 @@ export class NewPublicationPage implements OnInit { 0.9 // quality ).then((picture) => { this.photoOrVideo = false; - let fileObject = { - FileBase64: picture, - FileExtension: this.removeTextBeforeSlash('jpeg', '/'), - OriginalFileName: 'imagem' - } const FileExtension = this.removeTextBeforeSlash('jpeg', '/') @@ -231,11 +228,6 @@ export class NewPublicationPage implements OnInit { 800, // maxHeight 0.9 // quality ).then((picture) => { - let fileObject = { - FileBase64: picture, - FileExtension: this.removeTextBeforeSlash('jpeg', '/'), - OriginalFileName: 'image' - } const FileExtension = this.removeTextBeforeSlash('jpeg', '/') @@ -278,7 +270,7 @@ export class NewPublicationPage implements OnInit { console.log("base64 :data:video/mp4;base64,",this.arrayBufferToBase64(await blob.arrayBuffer())) */ - this.convertBlobToBase64(blobFile.blob).then((value) => { + this.convertBlobToBase64(blobFile.blob).then((value: string) => { console.log(value) @@ -294,7 +286,8 @@ export class NewPublicationPage implements OnInit { base64: value, extension: FileExtension, blobFile: file, - FileType: this.checkFileType.checkFileType(FileExtension) + FileType: this.checkFileType.checkFileType(FileExtension) as any, + OriginalFileName: 'load video' } ) @@ -368,7 +361,7 @@ export class NewPublicationPage implements OnInit { base64: content.data, extension: this.removeTextBeforeSlash(element.mimeType, '/'), OriginalFileName: 'video', - FileType: this.checkFileType.checkFileType( this.removeTextBeforeSlash(element.mimeType, '/')) + FileType: this.checkFileType.checkFileType( this.removeTextBeforeSlash(element.mimeType, '/')) as any }) this.seletedContent.push(fileObject) }) @@ -468,11 +461,10 @@ export class NewPublicationPage implements OnInit { console.log("this.publication.Files", this.publication.Files) } - - this.publication.Files = this.publication.Files.map(e => ({ - FileBase64: e.FileBase64, + this.publication.Files = this.publication.Files.map( (e: PublicationAttachmentEntity) => ({ + FileBase64: e.Base64, FileExtension: e.FileExtension, - OriginalFileName: 'foto' + OriginalFileName: e.OriginalFileName || 'foto' })) try { @@ -523,23 +515,21 @@ export class NewPublicationPage implements OnInit { if(e.FileType == 'video') { e.OriginalFileName = e.chucksManager.path e.FileExtension = "mp4" + e.Base64 = "" } return e }) - console.log("this.publication.Files", this.publication.Files) } - this.publication.Files = this.publication.Files.map(e => ({ - FileBase64: e.FileBase64, + this.publication.Files = this.publication.Files.map( (e:PublicationAttachmentEntity) => ({ + FileBase64: e.Base64, FileExtension: e.FileExtension, - OriginalFileName: 'foto' + OriginalFileName: e.OriginalFileName || 'foto' })) - - const loader = this.toastService.loading() try { @@ -560,12 +550,17 @@ export class NewPublicationPage implements OnInit { loader.remove() } - } else { this.toastService._badRequest("É necessário adicionar uma imagem ou vídeo") } } + + this.publicationFormMV.ObjectMergeNotification.close() + } + + ngOnDestroy() { + this.publicationFormMV.ObjectMergeNotification.close() } close() { @@ -1071,444 +1066,3 @@ export class NewPublicationPage implements OnInit { return true; } } - -class UploadFileUseCase { - CMAPIService: CMAPIService = window["CMAPIAPIRepository"] - constructor() {} - async execute(PublicationAttachmentEntity: PublicationAttachmentEntity): Promise> { - return new Promise(async (resolve, reject) => { - - PublicationAttachmentEntity.chucksManager.clearManualRetry() - - let path: string; - const length = PublicationAttachmentEntity.chucksManager.chunks.totalChunks.toString() - - const readAndUploadChunk = async(index: number) => { - - const chunk = await PublicationAttachmentEntity.chucksManager.chunks.getChunks(index) - const blob = new Blob([chunk]); - const blobFile = new File([blob], "test.mp4", { type: blob.type }); - - return this.CMAPIService.FileContent({length, path: PublicationAttachmentEntity.chucksManager.path, index, blobFile}) - } - - if(!PublicationAttachmentEntity.chucksManager.hasPath()) { - const initIndex = 1 - - const uploadRequest = await readAndUploadChunk(initIndex) - - if(uploadRequest.isOk()) { - - path = uploadRequest.value.data - - PublicationAttachmentEntity.chucksManager.setPath(path) - PublicationAttachmentEntity.chucksManager.setResponse(initIndex, uploadRequest) - } else { - PublicationAttachmentEntity.chucksManager.setManualRetry() - return reject(err(PublicationAttachmentEntity)) - } - } - - const allRequest: Promise[] = [] - let connection = true - - for (let index = 2; ( (index <= PublicationAttachmentEntity.chucksManager.chunks.totalChunks -1) && connection ); index++) { - const needUpload = PublicationAttachmentEntity.chucksManager.needToUploadChunkIndex(index) - - if(needUpload) { - - const request = readAndUploadChunk(index).then(async(uploadRequest) => { - if(uploadRequest.isErr()) { - const pingRequest = await this.CMAPIService.ping() - if( pingRequest.isErr()) { - connection = false - PublicationAttachmentEntity.chucksManager.setManualRetry() - return reject(err(PublicationAttachmentEntity)) - } - } else { - PublicationAttachmentEntity.chucksManager.setResponse(index, uploadRequest) - } - }) - allRequest.push(request) - - // const request = readAndUploadChunk(index) - // const uploadRequest = await request - - // allRequest.push(request) - // if(uploadRequest.isErr()) { - // const pingRequest = await this.CMAPIService.ping() - // if( pingRequest.isErr()) { - // reject(err(PublicationAttachmentEntity)) - // } - // } else { - // PublicationAttachmentEntity.chucksManager.setResponse(index, uploadRequest) - // } - } - } - - if(!connection) { - PublicationAttachmentEntity.chucksManager.setManualRetry() - return reject(err(PublicationAttachmentEntity)) - } else { - await Promise.all(allRequest) - - const uploadRequest = await readAndUploadChunk(PublicationAttachmentEntity.chucksManager.chunks.totalChunks) - if(uploadRequest.isErr()) { - const pingRequest = await this.CMAPIService.ping() - if( pingRequest.isErr()) { - PublicationAttachmentEntity.chucksManager.setManualRetry() - return reject(err(PublicationAttachmentEntity)) - } - } else { - PublicationAttachmentEntity.chucksManager.setResponse(PublicationAttachmentEntity.chucksManager.chunks.totalChunks, uploadRequest) - } - - PublicationAttachmentEntity.chucksManager.doneChunkUpload() - resolve(ok(PublicationAttachmentEntity)) - } - - - }) - } -} - -class PublicationAttachmentEntity { - FileBase64: string - FileExtension: string - FileType: 'image' | 'video' - OriginalFileName: string - blobFile: File - toUpload = false; - chucksManager : ChucksManager - - constructor({base64, extension, blobFile = null, OriginalFileName = null, FileType}) { - this.FileBase64 = base64; - this.FileExtension = extension; - this.blobFile = blobFile - this.OriginalFileName = OriginalFileName - this.FileType = FileType - - this.fixFileBase64(); - } - - fixFileBase64() { - if(this.FileType == 'image' ) { - if(!this.FileBase64.startsWith('data:')) { - this.FileBase64 = 'data:image/jpg;base64,' + this.FileBase64 - } - } else if (this.FileType == 'video' ) { - if(!this.FileBase64.startsWith('data:') && !this.FileBase64.startsWith('http')) { - this.FileBase64 = 'data:video/mp4;base64,' + this.FileBase64 - } - - } - } - - needUpload() { - this.toUpload = true - } - - setChunkManger (chunks: Chunks) { - this.chucksManager = new ChucksManager({chunks}) - } - get hasChunkManger() { - return this.chucksManager?.chunks - } - - get hasChunkManager() { - return this.chucksManager != null - } -} - -interface IPublicationFormModelEntity { - DateIndex: any - DocumentId: any - ProcessId: any - Title: any - Message: any - DatePublication: any - Files: PublicationAttachmentEntity[] -} - -class PublicationFormModel implements IPublicationFormModelEntity { - constructor() {} - DateIndex: any; - DocumentId: any; - ProcessId: any; - Title: any; - Message: any; - DatePublication: any; - OriginalFileName: string; - Files: PublicationAttachmentEntity[]; - - hasSet = false - - setData(data: IPublicationFormModelEntity) { - if(!this.hasSet) { - Object.assign(this, data) - } - - this.hasSet = true - } -} - -class PublicationFormMV { - - private UploadFileUseCase = new UploadFileUseCase() - private form = new PublicationFormModel() - private ObjectMergeNotification = new ObjectMergeNotification() - - setDataToFrom(data: IPublicationFormModelEntity) { - this.form.setData(data) - } - - private getVideoFiles() { - return this.form.Files.filter( x => x.FileType == 'video') - } - - private upload(PublicationAttachmentEntity: PublicationAttachmentEntity) { - - return new Promise(async (resolve, reject)=> { - - if(!PublicationAttachmentEntity.hasChunkManger) { - const fileBlob = PublicationAttachmentEntity.blobFile; - const fileChunks = new Chunks({chunkSize: 500 }) - fileChunks.setFile(fileBlob) - - PublicationAttachmentEntity.setChunkManger(fileChunks) - - PublicationAttachmentEntity.chucksManager.registerOnLastChunk(()=> { - const guid = PublicationAttachmentEntity.chucksManager.path - - this.ObjectMergeNotification.subscribe(guid, (data) => { - // console.log("data", data) - PublicationAttachmentEntity - resolve(true) - }) - }) - - } else { - if(PublicationAttachmentEntity.chucksManager.doneUpload) { - return resolve(true) - } - } - - const result = await this.UploadFileUseCase.execute(PublicationAttachmentEntity) - - if(result.isErr()) { - reject(false) - } - }) - - } - - uploadVideosFiles(): Promise { - return new Promise((resolve, reject)=> { - const videosFiles = this.getVideoFiles() - - const videosFilesToUploads = videosFiles.filter( e => e.toUpload == true) - - const Promises: Promise[] = [] - - for(const file of videosFilesToUploads) { - const promise = this.upload(file) - Promises.push(promise) - } - - // Use Promise.all to wait for all promises to resolve - Promise.all(Promises) - .then((results) => { - // Check if every promise resolved successfully - const allPromisesResolvedSuccessfully = results.every((result) => result == true); - - if (allPromisesResolvedSuccessfully) { - console.log('All promises resolved successfully.'); - resolve(true) - } else { - reject(false) - console.log('Some promises failed to resolve successfully.'); - } - }) - .catch((error) => { - reject(false) - console.error('An error occurred while resolving promises:', error); - }); - }) - - } -} - - -class Chunks { - - chunkSize: number - private file: File - - constructor({chunkSize}) { - this.chunkSize = chunkSize * 1024 - } - - get totalChunks () { - return Math.ceil(this.file.size / this.chunkSize); - } - - // Function to read a chunk of the file - readChunk(start: number, end: number): Promise { - const file = this.file - - return new Promise((resolve, reject) => { - const reader = new FileReader(); - reader.onload = () => { - if (reader.result instanceof ArrayBuffer) { - resolve(reader.result); - } else { - reject(new Error("Failed to read chunk")); - } - }; - - reader.readAsArrayBuffer(file.slice(start, end)); - }); - } - - setFile(file: File) { - this.file = file - } - - async getChunks(i: number) { - i-- - if(i < this.totalChunks) { - const start = i * this.chunkSize; - const end = Math.min(start + this.chunkSize, this.file.size); - const chunk = await this.readChunk(start, end); - - return chunk - } - } - -} - -interface IUploadResponse { - result: Result - attemp: number -} - -class ChucksManager { - chunks: Chunks - uploads: {[key: string]: IUploadResponse } = {} - path: string = undefined - uploadPercentage: string = "0%" - merging = false - onSetPath: Function[] = [] - onSetLastChunk: Function[] = [] - contentReady = false - manualRetry = false - - getUploadPercentage() { - return this.uploadPercentage - } - - get uploadsCount() { - return Object.entries(this.uploads).length - } - - get uploadWithSuccessCount() { - const uploadWithSuccess = Object.entries(this.uploads).filter(([index, data])=> data.result.isOk()) - return uploadWithSuccess.length - } - - get doneUpload() { - return this.chunks.totalChunks == this.uploadWithSuccessCount - } - - uploadFunc: Function - constructor({chunks}) { - this.chunks = chunks - } - - calculatePercentage(): number { - /** - * Calculate the percentage based on the total and current values. - * - * @param total - The total value. - * @param current - The current value. - * @returns The percentage calculated as (current / total) * 100. - */ - const total = this.chunks.totalChunks - const current = this.uploadWithSuccessCount - - if (total === 0) { - return 0; // To avoid division by zero error - } - - const percentage: number = (current / total) * 100; - return percentage; - } - - setManualRetry() { - this.manualRetry = true - } - - clearManualRetry() { - this.manualRetry = false - } - - setPercentage() { - const percentage: number = this.calculatePercentage() - console.log({percentage}) - this.uploadPercentage = percentage.toString()+"%" - } - - setPath(path: string) { - this.path = path - this.onSetPath.forEach(callback => callback()); - } - - registerOnSetPath(a: Function) { - this.onSetPath.push(a) - } - - registerOnLastChunk(a: Function) { - this.onSetPath.push(a) - } - - hasPath() { - return this.path != undefined - } - - isIndexRegistered(index) { - if(!this.uploads[index]) { - return false - } - return true - } - - needToUploadChunkIndex(index) { - return !this.uploads?.[index]?.result?.isOk() - } - - setResponse(index, UploadResponse) { - - if(!this.isIndexRegistered(index)) { - this.uploads[index] = { - attemp: 1, - result: UploadResponse - } - console.log({UploadResponse}) - } else { - this.uploads[index].attemp++; - this.uploads[index].result = UploadResponse - } - - this.setPercentage() - } - - doneChunkUpload() { - this.merging = true - this.onSetLastChunk.forEach(callback => callback()); - } - - contentSetReady() { - this.merging = false - this.contentReady = true - } -} diff --git a/src/app/shared/publication/upload/upload-streaming.service.spec.ts b/src/app/shared/publication/upload/upload-streaming.service.spec.ts new file mode 100644 index 000000000..338c21737 --- /dev/null +++ b/src/app/shared/publication/upload/upload-streaming.service.spec.ts @@ -0,0 +1,16 @@ +import { TestBed } from '@angular/core/testing'; + +import { UploadStreamingService } from './upload-streaming.service'; + +describe('UploadStreamingService', () => { + let service: UploadStreamingService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(UploadStreamingService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/src/app/shared/publication/upload/upload-streaming.service.ts b/src/app/shared/publication/upload/upload-streaming.service.ts new file mode 100644 index 000000000..aa6b0c1cf --- /dev/null +++ b/src/app/shared/publication/upload/upload-streaming.service.ts @@ -0,0 +1,483 @@ +import { Injectable } from '@angular/core'; +import { ok, err, Result } from 'neverthrow'; +import { ObjectMergeNotification } from 'src/app/services/socket-connection-mcr.service'; +import { CMAPIService } from "src/app/shared/repository/CMAPI/cmapi.service" +@Injectable({ + providedIn: 'root' +}) +export class UploadStreamingService { + constructor( private CMAPIService: CMAPIService,) { } +} + + +class UploadFileUseCase { + CMAPIService: CMAPIService = window["CMAPIAPIRepository"] + constructor() {} + async execute(PublicationAttachmentEntity: PublicationAttachmentEntity): Promise> { + return new Promise(async (resolve, reject) => { + + PublicationAttachmentEntity.chucksManager.clearManualRetry() + + let path: string; + const length = PublicationAttachmentEntity.chucksManager.chunks.totalChunks.toString() + + const readAndUploadChunk = async(index: number) => { + + const chunk = await PublicationAttachmentEntity.chucksManager.chunks.getChunks(index) + const blob = new Blob([chunk]); + const blobFile = new File([blob], "test.mp4", { type: blob.type }); + + return this.CMAPIService.FileContent({length, path: PublicationAttachmentEntity.chucksManager.path, index, blobFile}) + } + + if(!PublicationAttachmentEntity.chucksManager.hasPath()) { + const initIndex = 1 + + const uploadRequest = await readAndUploadChunk(initIndex) + + if(uploadRequest.isOk()) { + + path = uploadRequest.value.data + + PublicationAttachmentEntity.chucksManager.setPath(path) + PublicationAttachmentEntity.chucksManager.setResponse(initIndex, uploadRequest) + } else { + PublicationAttachmentEntity.chucksManager.setManualRetry() + return reject(err(PublicationAttachmentEntity)) + } + } + + const allRequest: Promise[] = [] + let connection = true + + for (let index = 2; ( (index <= PublicationAttachmentEntity.chucksManager.chunks.totalChunks -1) && connection ); index++) { + const needUpload = PublicationAttachmentEntity.chucksManager.needToUploadChunkIndex(index) + + if(needUpload) { + + const request = readAndUploadChunk(index).then(async(uploadRequest) => { + if(uploadRequest.isErr()) { + const pingRequest = await this.CMAPIService.ping() + if( pingRequest.isErr()) { + connection = false + PublicationAttachmentEntity.chucksManager.setManualRetry() + return reject(err(PublicationAttachmentEntity)) + } + } else { + PublicationAttachmentEntity.chucksManager.setResponse(index, uploadRequest) + } + }) + allRequest.push(request) + + // const request = readAndUploadChunk(index) + // const uploadRequest = await request + + // allRequest.push(request) + // if(uploadRequest.isErr()) { + // const pingRequest = await this.CMAPIService.ping() + // if( pingRequest.isErr()) { + // reject(err(PublicationAttachmentEntity)) + // } + // } else { + // PublicationAttachmentEntity.chucksManager.setResponse(index, uploadRequest) + // } + } + } + + if(!connection) { + PublicationAttachmentEntity.chucksManager.setManualRetry() + return reject(err(PublicationAttachmentEntity)) + } else { + await Promise.all(allRequest) + + const uploadRequest = await readAndUploadChunk(PublicationAttachmentEntity.chucksManager.chunks.totalChunks) + if(uploadRequest.isErr()) { + const pingRequest = await this.CMAPIService.ping() + if( pingRequest.isErr()) { + PublicationAttachmentEntity.chucksManager.setManualRetry() + return reject(err(PublicationAttachmentEntity)) + } + } else { + PublicationAttachmentEntity.chucksManager.setResponse(PublicationAttachmentEntity.chucksManager.chunks.totalChunks, uploadRequest) + } + + PublicationAttachmentEntity.chucksManager.doneChunkUpload() + resolve(ok(PublicationAttachmentEntity)) + } + + + }) + } +} + +export class PublicationAttachmentEntity { + url: string + FileExtension: string + FileType: 'image' | 'video' + OriginalFileName: string + blobFile?: File + toUpload = false; + chucksManager : ChucksManager + Base64: string + + constructor({base64, extension, blobFile, OriginalFileName, FileType}:PublicationAttachmentEntityParams) { + this.Base64 = base64; + this.FileExtension = extension; + this.blobFile = blobFile + this.OriginalFileName = OriginalFileName + this.FileType = FileType + + this.fixFileBase64(); + } + + fixFileBase64() { + if(this.FileType == 'image' ) { + if(!this.Base64.startsWith('data:')) { + this.url = 'data:image/jpg;base64,' + this.Base64 + } else { + this.url = this.Base64 + } + } else if (this.FileType == 'video' ) { + if(!this.Base64.startsWith('data:') && !this.Base64.startsWith('http')) { + this.url = 'data:video/mp4;base64,' + this.Base64 + } else { + this.url = this.Base64 + } + } + } + + needUpload() { + this.toUpload = true + } + + setChunkManger (chunks: Chunks) { + this.chucksManager = new ChucksManager({chunks}) + } + get hasChunkManger() { + return this.chucksManager?.chunks + } + + get hasChunkManager() { + return this.chucksManager != null + } +} + +interface IPublicationFormModelEntity { + DateIndex: any + DocumentId: any + ProcessId: any + Title: any + Message: any + DatePublication: any + Files: PublicationAttachmentEntity[] +} + +interface PublicationAttachmentEntityParams { + base64: string, + blobFile?: File + extension: string, + OriginalFileName: string, + FileType: 'image' | 'video' +} + +export class PublicationFormModel implements IPublicationFormModelEntity { + constructor() {} + DateIndex: any; + DocumentId: any; + ProcessId: any; + Title: any; + Message: any; + DatePublication: any; + OriginalFileName: string; + Files: PublicationAttachmentEntity[]; + + hasSet = false + + setData(data: IPublicationFormModelEntity) { + if(!this.hasSet) { + Object.assign(this, data) + } + + this.hasSet = true + } +} + +export class PublicationFormMV { + + private UploadFileUseCase = new UploadFileUseCase() + private form = new PublicationFormModel() + ObjectMergeNotification = new ObjectMergeNotification() + + setDataToFrom(data: IPublicationFormModelEntity) { + this.form.setData(data) + } + + private getVideoFiles() { + return this.form.Files.filter( x => x.FileType == 'video') + } + + private upload(PublicationAttachmentEntity: PublicationAttachmentEntity) { + + return new Promise(async (resolve, reject)=> { + + if(!PublicationAttachmentEntity.hasChunkManger) { + const fileBlob = PublicationAttachmentEntity.blobFile; + const fileChunks = new Chunks({chunkSize: 500 }) + fileChunks.setFile(fileBlob) + + PublicationAttachmentEntity.setChunkManger(fileChunks) + + PublicationAttachmentEntity.chucksManager.registerOnLastChunk(()=> { + const guid = PublicationAttachmentEntity.chucksManager.path + + this.ObjectMergeNotification.subscribe(guid, (data) => { + // console.log("data", data) + PublicationAttachmentEntity + resolve(true) + }) + }) + + } else { + if(PublicationAttachmentEntity.chucksManager.doneUpload) { + return resolve(true) + } + } + + if( PublicationAttachmentEntity.chucksManager.isUploading == false) { + PublicationAttachmentEntity.chucksManager.setUploading() + const result = await this.UploadFileUseCase.execute(PublicationAttachmentEntity) + PublicationAttachmentEntity.chucksManager.clearUploading() + + if(result.isErr()) { + reject(false) + } + } + + }) + + } + + uploadVideosFiles(): Promise { + return new Promise((resolve, reject)=> { + const videosFiles = this.getVideoFiles() + + const videosFilesToUploads = videosFiles.filter( e => e.FileType == "video") + + const Promises: Promise[] = [] + + for(const file of videosFilesToUploads) { + const promise = this.upload(file) + Promises.push(promise) + } + + // Use Promise.all to wait for all promises to resolve + Promise.all(Promises) + .then((results) => { + // Check if every promise resolved successfully + const allPromisesResolvedSuccessfully = results.every((result) => result == true); + + if (allPromisesResolvedSuccessfully) { + console.log('All promises resolved successfully.'); + resolve(true) + } else { + reject(false) + console.log('Some promises failed to resolve successfully.'); + } + }) + .catch((error) => { + reject(false) + console.error('An error occurred while resolving promises:', error); + }); + }) + + } +} + + +export class Chunks { + + chunkSize: number + private file: File + + constructor({chunkSize}) { + this.chunkSize = chunkSize * 1024 + } + + get totalChunks () { + return Math.ceil(this.file.size / this.chunkSize); + } + + // Function to read a chunk of the file + readChunk(start: number, end: number): Promise { + const file = this.file + + return new Promise((resolve, reject) => { + const reader = new FileReader(); + reader.onload = () => { + if (reader.result instanceof ArrayBuffer) { + resolve(reader.result); + } else { + reject(new Error("Failed to read chunk")); + } + }; + + reader.readAsArrayBuffer(file.slice(start, end)); + }); + } + + setFile(file: File) { + this.file = file + } + + async getChunks(i: number) { + i-- + if(i < this.totalChunks) { + const start = i * this.chunkSize; + const end = Math.min(start + this.chunkSize, this.file.size); + const chunk = await this.readChunk(start, end); + + return chunk + } + } + +} + +interface IUploadResponse { + result: Result + attemp: number +} + +export class ChucksManager { + chunks: Chunks + uploads: {[key: string]: IUploadResponse } = {} + path: string = undefined + uploadPercentage: string = "0%" + merging = false + onSetPath: Function[] = [] + onSetLastChunk: Function[] = [] + contentReady = false + manualRetry = false + isUploading = false + subscribeToUseCaseResponse: Function[] = [] + + getUploadPercentage() { + return this.uploadPercentage + } + + get uploadsCount() { + return Object.entries(this.uploads).length + } + + get uploadWithSuccessCount() { + const uploadWithSuccess = Object.entries(this.uploads).filter(([index, data])=> data.result.isOk()) + return uploadWithSuccess.length + } + + get doneUpload() { + return this.chunks.totalChunks == this.uploadWithSuccessCount + } + + uploadFunc: Function + constructor({chunks}) { + this.chunks = chunks + } + + calculatePercentage(): number { + /** + * Calculate the percentage based on the total and current values. + * + * @param total - The total value. + * @param current - The current value. + * @returns The percentage calculated as (current / total) * 100. + */ + const total = this.chunks.totalChunks + const current = this.uploadWithSuccessCount + + if (total === 0) { + return 0; // To avoid division by zero error + } + + const percentage: number = (current / total) * 100; + return percentage; + } + + setManualRetry() { + this.manualRetry = true + } + + clearManualRetry() { + this.manualRetry = false + } + + setUploading() { + this.isUploading = true + } + clearUploading() { + this.isUploading = false + } + + setPercentage() { + const percentage: number = this.calculatePercentage() + console.log({percentage}) + this.uploadPercentage = percentage.toString()+"%" + } + + setPath(path: string) { + this.path = path + this.onSetPath.forEach(callback => callback()); + } + + registerOnSetPath(a: Function) { + this.onSetPath.push(a) + } + + registerOnLastChunk(a: Function) { + this.onSetPath.push(a) + } + + registerToUseCaseResponse(a: Function) { + this.subscribeToUseCaseResponse.push(a) + } + + + hasPath() { + return this.path != undefined + } + + isIndexRegistered(index) { + if(!this.uploads[index]) { + return false + } + return true + } + + needToUploadChunkIndex(index) { + return !this.uploads?.[index]?.result?.isOk() + } + + setResponse(index, UploadResponse) { + + if(!this.isIndexRegistered(index)) { + this.uploads[index] = { + attemp: 1, + result: UploadResponse + } + console.log({UploadResponse}) + } else { + this.uploads[index].attemp++; + this.uploads[index].result = UploadResponse + } + + this.setPercentage() + } + + doneChunkUpload() { + this.merging = true + this.onSetLastChunk.forEach(callback => callback()); + } + + contentSetReady() { + this.merging = false + this.contentReady = true + } +}