This commit is contained in:
Peter Maquiran
2024-02-23 12:39:59 +01:00
29 changed files with 1742 additions and 877 deletions
+82 -2
View File
@@ -49,7 +49,12 @@
"@capacitor/share": "^4.1.0",
"@capacitor/storage": "^1.2.5",
"@capawesome/capacitor-file-picker": "^5.3.0",
<<<<<<< HEAD
"@capawesome/capacitor-screen-orientation": "^5.0.1",
=======
"@ffmpeg/core": "^0.12.6",
"@ffmpeg/ffmpeg": "^0.12.10",
>>>>>>> feature/upload-streaming-videos
"@fortawesome/angular-fontawesome": "^0.9.0",
"@fortawesome/fontawesome-free": "^5.15.3",
"@fortawesome/fontawesome-svg-core": "^1.2.35",
@@ -154,6 +159,7 @@
"lite-server": "^2.6.1",
"minisearch": "^6.0.1",
"moment": "^2.29.3",
"mux.js": "^6.3.0",
"neverthrow": "^6.1.0",
"ng-lazyload-image": "^9.1.2",
"ng2-pdf-viewer": "^3.0.8",
@@ -3616,7 +3622,6 @@
"version": "7.14.8",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.14.8.tgz",
"integrity": "sha512-twj3L8Og5SaCRCErB4x4ajbvBIVV77CGeFglHpeg5WC5FF8TZzBWXtTJ4MqaD9QszLYTtr+IsaAL2rEUevb+eg==",
"dev": true,
"dependencies": {
"regenerator-runtime": "^0.13.4"
},
@@ -4022,6 +4027,7 @@
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/@capawesome/capacitor-file-picker/-/capacitor-file-picker-5.3.0.tgz",
"integrity": "sha512-r+cfD+9FCBXMYtckSovgi7WoMWT5cxzNDaH3SDNuiORiyQklOl+7DExfhW00p1DqsDR+f50nADk/NBb4IsGVYg==",
<<<<<<< HEAD
"funding": [
{
"type": "github",
@@ -4040,6 +4046,8 @@
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/@capawesome/capacitor-screen-orientation/-/capacitor-screen-orientation-5.0.1.tgz",
"integrity": "sha512-IaF7HjlxS1EXjUNCa8Ztu9EpxsplAYsNcDUHVX2x3B+KRqB0f7DQYi96cnKYeWpMt9JhrLPXc8XGRP4suU4NYw==",
=======
>>>>>>> feature/upload-streaming-videos
"funding": [
{
"type": "github",
@@ -4250,6 +4258,33 @@
"node": ">=10.0.0"
}
},
"node_modules/@ffmpeg/core": {
"version": "0.12.6",
"resolved": "https://registry.npmjs.org/@ffmpeg/core/-/core-0.12.6.tgz",
"integrity": "sha512-PrjWBTfGn2WVn9T7wGnzfFwChbqWeZc7tM9vvJZVRadYFUDakfzy7W0LpYC0cvvK0xT82qlBsk38lQhJ/Hps5A==",
"engines": {
"node": ">=16.x"
}
},
"node_modules/@ffmpeg/ffmpeg": {
"version": "0.12.10",
"resolved": "https://registry.npmjs.org/@ffmpeg/ffmpeg/-/ffmpeg-0.12.10.tgz",
"integrity": "sha512-lVtk8PW8e+NUzGZhPTWj2P1J4/NyuCrbDD3O9IGpSeLYtUZKBqZO8CNj1WYGghep/MXoM8e1qVY1GztTkf8YYQ==",
"dependencies": {
"@ffmpeg/types": "^0.12.2"
},
"engines": {
"node": ">=18.x"
}
},
"node_modules/@ffmpeg/types": {
"version": "0.12.2",
"resolved": "https://registry.npmjs.org/@ffmpeg/types/-/types-0.12.2.tgz",
"integrity": "sha512-NJtxwPoLb60/z1Klv0ueshguWQ/7mNm106qdHkB4HL49LXszjhjCCiL+ldHJGQ9ai2Igx0s4F24ghigy//ERdA==",
"engines": {
"node": ">=16.x"
}
},
"node_modules/@firebase/analytics": {
"version": "0.6.18",
"resolved": "https://registry.npmjs.org/@firebase/analytics/-/analytics-0.6.18.tgz",
@@ -26836,6 +26871,22 @@
"resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz",
"integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA=="
},
"node_modules/mux.js": {
"version": "6.3.0",
"resolved": "https://registry.npmjs.org/mux.js/-/mux.js-6.3.0.tgz",
"integrity": "sha512-/QTkbSAP2+w1nxV+qTcumSDN5PA98P0tjrADijIzQHe85oBK3Akhy9AHlH0ne/GombLMz1rLyvVsmrgRxoPDrQ==",
"dependencies": {
"@babel/runtime": "^7.11.2",
"global": "^4.4.0"
},
"bin": {
"muxjs-transmux": "bin/transmux.js"
},
"engines": {
"node": ">=8",
"npm": ">=5"
}
},
"node_modules/nan": {
"version": "2.17.0",
"resolved": "https://registry.npmjs.org/nan/-/nan-2.17.0.tgz",
@@ -46204,7 +46255,6 @@
"version": "7.14.8",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.14.8.tgz",
"integrity": "sha512-twj3L8Og5SaCRCErB4x4ajbvBIVV77CGeFglHpeg5WC5FF8TZzBWXtTJ4MqaD9QszLYTtr+IsaAL2rEUevb+eg==",
"dev": true,
"requires": {
"regenerator-runtime": "^0.13.4"
}
@@ -46516,12 +46566,15 @@
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/@capawesome/capacitor-file-picker/-/capacitor-file-picker-5.3.0.tgz",
"integrity": "sha512-r+cfD+9FCBXMYtckSovgi7WoMWT5cxzNDaH3SDNuiORiyQklOl+7DExfhW00p1DqsDR+f50nADk/NBb4IsGVYg==",
<<<<<<< HEAD
"requires": {}
},
"@capawesome/capacitor-screen-orientation": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/@capawesome/capacitor-screen-orientation/-/capacitor-screen-orientation-5.0.1.tgz",
"integrity": "sha512-IaF7HjlxS1EXjUNCa8Ztu9EpxsplAYsNcDUHVX2x3B+KRqB0f7DQYi96cnKYeWpMt9JhrLPXc8XGRP4suU4NYw==",
=======
>>>>>>> feature/upload-streaming-videos
"requires": {}
},
"@cnakazawa/watch": {
@@ -46684,6 +46737,24 @@
"integrity": "sha512-Fxt+AfXgjMoin2maPIYzFZnQjAXjAL0PHscM5pRTtatFqB+vZxAM9tLp2Optnuw3QOQC40jTNeGYFOMvyf7v9g==",
"dev": true
},
"@ffmpeg/core": {
"version": "0.12.6",
"resolved": "https://registry.npmjs.org/@ffmpeg/core/-/core-0.12.6.tgz",
"integrity": "sha512-PrjWBTfGn2WVn9T7wGnzfFwChbqWeZc7tM9vvJZVRadYFUDakfzy7W0LpYC0cvvK0xT82qlBsk38lQhJ/Hps5A=="
},
"@ffmpeg/ffmpeg": {
"version": "0.12.10",
"resolved": "https://registry.npmjs.org/@ffmpeg/ffmpeg/-/ffmpeg-0.12.10.tgz",
"integrity": "sha512-lVtk8PW8e+NUzGZhPTWj2P1J4/NyuCrbDD3O9IGpSeLYtUZKBqZO8CNj1WYGghep/MXoM8e1qVY1GztTkf8YYQ==",
"requires": {
"@ffmpeg/types": "^0.12.2"
}
},
"@ffmpeg/types": {
"version": "0.12.2",
"resolved": "https://registry.npmjs.org/@ffmpeg/types/-/types-0.12.2.tgz",
"integrity": "sha512-NJtxwPoLb60/z1Klv0ueshguWQ/7mNm106qdHkB4HL49LXszjhjCCiL+ldHJGQ9ai2Igx0s4F24ghigy//ERdA=="
},
"@firebase/analytics": {
"version": "0.6.18",
"resolved": "https://registry.npmjs.org/@firebase/analytics/-/analytics-0.6.18.tgz",
@@ -64286,6 +64357,15 @@
"resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz",
"integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA=="
},
"mux.js": {
"version": "6.3.0",
"resolved": "https://registry.npmjs.org/mux.js/-/mux.js-6.3.0.tgz",
"integrity": "sha512-/QTkbSAP2+w1nxV+qTcumSDN5PA98P0tjrADijIzQHe85oBK3Akhy9AHlH0ne/GombLMz1rLyvVsmrgRxoPDrQ==",
"requires": {
"@babel/runtime": "^7.11.2",
"global": "^4.4.0"
}
},
"nan": {
"version": "2.17.0",
"resolved": "https://registry.npmjs.org/nan/-/nan-2.17.0.tgz",
+4 -1
View File
@@ -11,6 +11,7 @@ import { environment } from 'src/environments/environment';
import { Storage } from '@ionic/storage';
import { ChatController } from './controller/chat';
import { register } from 'swiper/element/bundle';
import { DomSanitizer } from '@angular/platform-browser';
const CUSTOM_DATE_FORMATS: NgxMatDateFormats = {
parse: {
@@ -39,9 +40,11 @@ export class AppComponent {
private statusBar: StatusBar,
public ThemeService: ThemeService,
private storage: Storage,
private ChatSystemService: ChatSystemService
private ChatSystemService: ChatSystemService,
private sanitizer: DomSanitizer
) {
window["sanitizer"] = this.sanitizer
this.initializeApp();
this.storage.set('version', environment.version).then(() => {})
ChatController.ChatSystemService = this.ChatSystemService
+14 -5
View File
@@ -22,6 +22,8 @@ export class TokenInterceptor implements HttpInterceptor {
null
);
private excludedDomains = ['Login', environment.apiChatUrl]; // Add the domains you want to exclude
constructor(private http: HttpClient, private router: Router,) { }
@@ -29,6 +31,11 @@ export class TokenInterceptor implements HttpInterceptor {
request: HttpRequest<any>,
next: HttpHandler
): Observable<HttpEvent<any>> {
if (this.shouldExcludeDomain(request)) {
return next.handle(request);
}
if (SessionStore.user.Authorization) {
request = this.addToken(request, SessionStore.user.Authorization);
}
@@ -44,11 +51,12 @@ export class TokenInterceptor implements HttpInterceptor {
);
}
private addToken(request: HttpRequest<any>, token: string) {
private shouldExcludeDomain(request: HttpRequest<any>): boolean {
const url = request.url.toLowerCase();
return this.excludedDomains.some((domain) => url.includes(domain.toLowerCase()));
}
if(request.url.includes("Login")) {
return request.clone({});
} else {
private addToken(request: HttpRequest<any>, token: string) {
return request.clone({
setHeaders: {
@@ -56,11 +64,11 @@ export class TokenInterceptor implements HttpInterceptor {
Authorization: `Bearer ${token}`,
},
});
}
}
private handle401Error(request: HttpRequest<any>, next: HttpHandler) {
// if not getting the new token yet
if (!this.isRefreshing) {
this.isRefreshing = true;
this.refreshTokenSubject.next(null);
@@ -73,6 +81,7 @@ export class TokenInterceptor implements HttpInterceptor {
})
);
} else {
// get new token
return this.refreshTokenSubject.pipe(
filter((token) => token != null),
take(1),
@@ -5,7 +5,7 @@
<fa-icon icon="chevron-left" class="menu-icon font-awesome-1"></fa-icon>
</div>
<div class="middle add-ellipsis">
<div class="middle ">
{{file.title}}
</div>
@@ -40,19 +40,23 @@
<ion-img *ngIf="checkFileType.checkFileType(seleted.FileExtension) == 'image'" [(ngModel)]="capturedImage"
name="image" ngDefaultControl [src]="'data:image/jpg;base64,' + seleted.FileBase64"
name="image" ngDefaultControl [src]="'data:image/jpg;base64,' + seleted.Base64"
(click)="imageSize(capturedImage)" style="height: 69px;"></ion-img>
<video *ngIf="checkFileType.checkFileType(seleted.FileExtension) == 'video'" width="70" height="70"
<video class="55" *ngIf="checkFileType.checkFileType(seleted.FileExtension) == 'video' && publicationTitle == ActionType.edit" width="70" height="70"
controls="controls" preload="metadata" autoplay="autoplay" webkit-playsinline="webkit-playsinline">
<source type="video/mp4" [src]="seleted.FileBase64">
<source type="video/mp4" [src]="'data:video/mp4;base64,' + seleted.Base64">
</video>
<video class="src" *ngIf="checkFileType.checkFileType(seleted.FileExtension) == 'video' && publicationTitle != ActionType.edit" width="70" height="70"
controls="controls" preload="metadata" autoplay="autoplay" webkit-playsinline="webkit-playsinline">
<source type="video/mp4" [src]="seleted.url">
</video>
</div>
<!-- Display the blurred image and count if there are more images -->
<ion-thumbnail *ngIf="seletedContent.length > displayLimit" lot="start">
<ion-img [src]="'data:image/jpg;base64,' + seletedContent[displayLimit - 1].base64"
<ion-img [src]="'data:image/jpg;base64,' + seletedContent[displayLimit - 1].Base64"
style="filter: blur(5px);"></ion-img>
<p>mais {{ seletedContent.length - displayLimit }}</p>
@@ -22,7 +22,6 @@ import { HttpErrorHandle } from 'src/app/services/http-error-handle.service';
import { PublicationFolderService } from 'src/app/store/publication-folder.service';
import { RouteService } from 'src/app/services/route.service';
import { FileService } from 'src/app/services/functions/file.service';
import { readAndCompressImage } from 'browser-image-resizer';
import { FilePicker } from '@capawesome/capacitor-file-picker';
import { CapacitorVideoPlayer } from 'capacitor-video-player';
import { CaptureError, CaptureImageOptions, MediaCapture, MediaFile } from '@awesome-cordova-plugins/media-capture/ngx';
@@ -31,9 +30,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 { App } from '@capacitor/app';
import { Router } from '@angular/router';
import { PublicationAttachmentEntity } from 'src/app/shared/publication/upload/upload-streaming.service';
const config = {
quality: 0.5,
maxWidth: 800,
@@ -95,6 +92,13 @@ export class NewPublicationPage implements OnInit {
publicationTitle: string;
imgUrl: any;
ActionType = {
newRapid : "1",
new: "2",
edit: "3"
}
Defaultimage: any = '';
photo: SafeResourceUrl;
@@ -115,7 +119,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;
@@ -136,8 +140,7 @@ export class NewPublicationPage implements OnInit {
public FileService: FileService,
private mediaCapture: MediaCapture,
public checkFileType: checkFileTypeService,
private FileValidatorService: FileValidatorService,
private router: Router,
private FileValidatorService: FileValidatorService
) {
this.publicationType = this.navParams.get('publicationType');
@@ -146,6 +149,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';
@@ -165,12 +180,16 @@ export class NewPublicationPage implements OnInit {
recursive: true
});
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
@@ -197,11 +216,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)
@@ -255,11 +277,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));
@@ -298,17 +323,28 @@ 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)
@@ -409,7 +445,6 @@ export class NewPublicationPage implements OnInit {
if (this.Form.invalid) return false
if (this.seletedContent.length != 0) {
if (this.publicationType == '3') {
const loader = this.toastService.loading()
@@ -538,13 +573,11 @@ export class NewPublicationPage implements OnInit {
try {
await this.publications.CreatePublication(this.folderId, this.publication).toPromise();
this.close();
this.httpErrorHandle.httpsSucessMessagge('Criar publicação')
if(window["sharedContent"]) {
this.router.navigate(['/home/publications', this.folderId]);
return
}
window["sharedContent"] = null;
window["endSharedContent"] = null;
this.close();
} catch (error) {
@@ -556,10 +589,6 @@ export class NewPublicationPage implements OnInit {
}
this.PublicationFolderService.getPublicationsIds(this.folderId)
} else {
this.httpErrorHandle.validationMessagge("noFileSelected")
}
}
@@ -567,10 +596,6 @@ export class NewPublicationPage implements OnInit {
this.modalController.dismiss(this.publication).then(() => {
this.showLoader = true;
});
if(window["sharedContent"]) {
this.closeApp();
}
}
clear() {
@@ -753,23 +778,15 @@ 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) => {
let fileObject;
if (this.checkFileType.checkFileType(FileExtension) == 'image') {
fileObject = {
FileBase64: this.removeTextBeforeSlash(content.data, ','),
FileExtension: FileExtension,
OriginalFileName: 'shared',
}
} else if (this.checkFileType.checkFileType(FileExtension) == 'video') {
fileObject = {
FileBase64: 'data:video/mp4;base64,' + 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
}
}
console.log('shared base', content.data)
)
this.seletedContent.push(fileObject)
})
@@ -791,9 +808,5 @@ export class NewPublicationPage implements OnInit {
this.seletedContent.splice(index, 1)
}
closeApp() {
App.exitApp()
}
}
@@ -19,12 +19,9 @@
<div class="title-content width-100 d-flex justify-space-between">
<div class="div-title flex-grow-1">
<ion-label class="title font-25-em">Acções</ion-label>
<!-- <div>
<input type="file" (change)="onFileSelect($event)" />
</div> -->
</div>
<div class="div-icon">
<div *ngIf="!intent" class="div-icon">
<button *ngIf="p.userPermission([p.permissionList.Actions.create])" title="Adicionar nova ação presidencial" class="btn-no-color" (click)="AddPublicationFolder()">
<ion-icon *ngIf="ThemeService.currentTheme == 'default' " slot="icon-only" src='assets/images/icons-add.svg'></ion-icon>
<ion-icon *ngIf="ThemeService.currentTheme == 'gov' " slot="icon-only" src='assets/images/theme/gov/icons-add.svg'></ion-icon>
@@ -34,12 +31,12 @@
<ion-icon class=" font-45-em" src="assets/images/theme/gov/icon-reload.svg"></ion-icon>
</button>
</div>
<!-- <div *ngIf="intent" class="div-icon">
<div *ngIf="intent" class="div-icon">
<button title="Atualizar" class="btn-no-color" (click)="close()">
<ion-icon class=" font-45-em" src="assets/images/icons-search-close.svg"></ion-icon>
</button>
</div> -->
</div>
</div>
</div>
+2 -1
View File
@@ -8,10 +8,11 @@ import { ExpedienteTaskPipe } from './expediente-task.pipe';
import { ParticipantsPipe } from './participants.pipe';
import { SafehtmlPipe } from './safehtml.pipe';
import { EventoApprovePipe } from './evento-approve.pipe';
import { SafePipe } from './safe.pipe';
@NgModule({
declarations: [FilterPipe, SearchDocumentPipe, CustomTaskPipe, EventPipe, PublicationPipe, ExpedienteTaskPipe, ParticipantsPipe, SafehtmlPipe, EventoApprovePipe],
declarations: [FilterPipe, SearchDocumentPipe, CustomTaskPipe, EventPipe, PublicationPipe, ExpedienteTaskPipe, ParticipantsPipe, SafehtmlPipe, EventoApprovePipe, SafePipe],
exports: [FilterPipe, SafehtmlPipe],
imports: []
})
+8
View File
@@ -0,0 +1,8 @@
import { SafePipe } from './safe.pipe';
describe('SafePipe', () => {
it('create an instance', () => {
const pipe = new SafePipe();
expect(pipe).toBeTruthy();
});
});
+13
View File
@@ -0,0 +1,13 @@
import { Pipe, PipeTransform } from '@angular/core';
import { DomSanitizer, SafeUrl } from '@angular/platform-browser';
@Pipe({
name: 'safe'
})
export class SafePipe implements PipeTransform {
constructor(private sanitizer: DomSanitizer) { }
transform(url: string): SafeUrl {
return this.sanitizer.bypassSecurityTrustUrl(url);
}
}
+421 -17
View File
@@ -1,46 +1,450 @@
import { Injectable } from '@angular/core';
import * as signalR from "@microsoft/signalr"
import { SessionStore } from '../store/session.service';
import { v4 as uuidv4 } from 'uuid'
import { HttpClient, HttpHeaders, HttpEventType } from '@angular/common/http';
import { CMAPIService } from '../shared/repository/CMAPI/cmapi.service';
import { HubConnectionBuilder } from '@microsoft/signalr';
import { ok, err, Result } from 'neverthrow';
@Injectable({
providedIn: 'root'
})
export class SocketConnectionMCRService {
// private callbacks: Function[] = []
// private onDisconnect: Function[] = []
// private onConnect: Function[] = []
constructor(private http: HttpClient, private _CMAPIService: CMAPIService) {
window["http"] = this.http
}
// 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);
// });
// }
// subscribe(callback) {
// this.callbacks.push(callback);
// }
// unsubscribe(callback) {
// this.callbacks = this.callbacks.filter(cb => cb !== callback);
// }
// onDisconnectCallback(callback) {
// this.onDisconnect.push(callback)
// }
// onConnectCallback(callback) {
// this.onConnect.push(callback)
// }
}
class ReconnectingWebSocketSignalR {
private connection: any
isOpen: boolean = false
private callbacks: Function[] = []
private onDisconnect: Function[] = []
private onConnect: Function[] = []
private whenConnected: Function[] = []
stop = true
constructor() {}
connect() {
console.log("try to connect=================================")
this.stop = false
var connection = new signalR.HubConnectionBuilder()
this.connection = new signalR.HubConnectionBuilder()
.withUrl("https://gdcmapi-dev.dyndns.info/FileHub", {
transport: signalR.HttpTransportType.LongPolling,
accessTokenFactory: () => 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()
this.connection.start()
.then(() => {
console.log("SignalR connection started.");
this.isOpen = true;
console.log('WebSocket connection established');
this.onConnect.forEach(callback => callback());
this.whenConnected.forEach(callback => callback())
})
.catch((error) => {
console.error("Error starting SignalR connection:", error);
});
connection.onclose((error) => {
connection.start()
console.log("SignalR connection closed:", error);
this.connection.on("ReceiveMessage", (message) => {
const data: any = JSON.parse(message)
console.log("ReceiveMessage", data)
this.callbacks.forEach(callback => callback(data));
})
this.connection.onclose((error) => {
console.log('WebSocket connection closed..');
this.isOpen = false;
this.onDisconnect.forEach(callback => callback());
// Attempt to reconnect after a delay
if(this.stop == false) {
setTimeout(() => {
this.connect();
}, 1000); // Adjust the delay as needed
}
});
}
commit(path): Promise<Result<true, false>> {
return new Promise((resolve, reject) => {
this.connection.invoke("CommitUpload", path).then((e) => {
console.log("commit message", e)
resolve(ok(true))
}).catch(err => {
resolve(err(false))
console.error(err.toString())
});
})
}
disconnect() {
this.stop = true
if(this.isOpen == true) {
this.connection.stop()
.then(() => {
console.log('WebSocket connection was closed by client');
this.isOpen = false;
this.onDisconnect.forEach(callback => callback());
console.log("SignalR connection stopped.");
})
.catch((error) => {
console.error("Error stopping SignalR connection by client:", error);
});
}
}
subscribe(callback) {
this.callbacks.push(callback);
}
unsubscribe(callback) {
this.callbacks = this.callbacks.filter(cb => cb !== callback);
}
onDisconnectCallback(callback) {
this.onDisconnect.push(callback)
}
onConnectCallback(callback) {
this.onConnect.push(callback)
}
registerWhenConnected(f: Function) {
if(this.isOpen) {
f();
} else {
this.whenConnected.push(f);
}
}
}
interface socketResponse {
index: string
Guid: string
IsCompleted: Boolean
}
// class ReconnectingWebSocket {
// private url: string
// private socket
// isOpen: boolean
// private callbacks: Function[] = []
// private onDisconnect: Function[] = []
// private onConnect: Function[] = []
// private whenConnected: Function[] = []
// private stop = true
// http: HttpClient = window["http"]
// constructor(url) {
// this.url = url;
// this.socket = null;
// this.isOpen = false;
// }
// connect() {
// this.socket = new WebSocket(this.url);
// 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());
// this.whenConnected.forEach(callback => callback())
// });
// 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
// if(this.stop == false) {
// 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.');
// }
// }
// disconnect() {
// this.stop = true
// if (this.isOpen) {
// this.isOpen = false;
// this.socket.close();
// }
// }
// subscribe(callback) {
// this.callbacks.push(callback);
// }
// unsubscribe(callback) {
// this.callbacks = this.callbacks.filter(cb => cb !== callback);
// }
// onDisconnectCallback(callback) {
// this.onDisconnect.push(callback)
// }
// onConnectCallback(callback) {
// this.onConnect.push(callback)
// }
// registerWhenConnected(f: Function) {
// if(this.isOpen) {
// f();
// } else {
// this.whenConnected.push(f);
// }
// }
// }
// export class ObjectMergeNotification{
// socket = new ReconnectingWebSocket('ws://localhost:3002');
// callbacks: {[GUID: string]: Function} = {}
// runWatch = true
// CMAPIService: CMAPIService = window["CMAPIAPIRepository"]
// watchCount = 0
// constructor() {
// this.socket.onDisconnectCallback(()=> {
// console.log("run watch")
// this.runWatch = true
// this.watch()
// })
// this.socket.onConnectCallback(()=> {
// console.log("open trigger")
// this.runWatch = false
// })
// this.socket.subscribe((data: socketResponse) => {
// if(data.IsCompleted == true) {
// console.log("==================!!!====================")
// try {
// this.callbacks[data.Guid](data)
// delete this.callbacks[data.Guid]
// } catch (error) {}
// } else {
// console.log("else", data)
// }
// })
// this.watch()
// }
// connect() {
// this.socket.connect()
// }
// async watch() {
// this.watchCount = 0;
// if(this.runWatch) {
// setTimeout(async () => {
// for(const [key, funx] of Object.entries(this.callbacks)) {
// const request = await this.CMAPIService.getVideoHeader(key)
// if(request.isOk()) {
// funx()
// delete this.callbacks[key]
// }
// }
// this.watchCount++
// if(this.watchCount <= 15) {
// this.watch()
// } else {
// this.runWatch = false
// }
// }, 1000)
// } else {
// console.log("end loop============================")
// }
// }
// close() {
// this.socket.disconnect();
// this.watchCount = 0;
// this.runWatch = false
// }
// subscribe(GUID, callback:Function) {
// this.callbacks[GUID] = callback;
// }
// unsubscribe(GUID) {
// delete this.callbacks[GUID]
// }
// }
export class ObjectMergeNotification{
socket = new ReconnectingWebSocketSignalR()
callbacks: {[GUID: string]: Function} = {}
runWatch = true
CMAPIService: CMAPIService = window["CMAPIAPIRepository"]
watchCount = 0
constructor() {
this.socket.onDisconnectCallback(()=> {
console.log("run watch")
this.runWatch = true
this.watch()
})
this.socket.onConnectCallback(()=> {
console.log("open trigger")
this.runWatch = false
})
this.socket.subscribe((data: socketResponse) => {
if(data.IsCompleted == true) {
console.log("==================!!!====================")
try {
this.callbacks[data.Guid](data)
delete this.callbacks[data.Guid]
} catch (error) {}
} else {
console.log("else", data)
}
})
this.socket.connect();
this.watch()
}
connect() {
this.socket.connect();
}
close() {
this.socket.disconnect();
this.watchCount = 0;
this.runWatch = false
}
async watch() {
// this.watchCount = 0;
// if(this.runWatch) {
// setTimeout(async () => {
// for(const [key, funx] of Object.entries(this.callbacks)) {
// const request = await this.CMAPIService.getVideoHeader(key)
// if(request.isOk()) {
// funx()
// delete this.callbacks[key]
// }
// }
// this.watchCount++
// if(this.watchCount <= 15) {
// this.watch()
// } else {
// this.runWatch = false
// }
// }, 1000)
// } else {
// console.log("end loop============================")
// }
}
subscribe(GUID, callback:Function) {
this.callbacks[GUID] = callback;
}
unsubscribe(GUID) {
delete this.callbacks[GUID]
}
}
-91
View File
@@ -12,97 +12,6 @@ export class StreamService {
window["StreamService"] = this
}
async uploadFile() {
const API_URL = 'http://localhost:3000/upload'; // Replace with your server URL
const filePath = 'path/to/large-file.zip'; // Replace with the path to your file
const fileName = 'my-file'; // Specify your desired filename
const fileExtension = 'zip'; // Specify the file extension
const headers = new HttpHeaders()
.append('X-File-Name', fileName)
.append('X-File-Extension', fileExtension);
const file = await this.readFileInChunks(filePath);
const chunkSize = 1024 * 1024; // 1 MB chunk size (adjust as needed)
for (let offset = 0; offset < file.length; offset += chunkSize) {
const chunk = file.slice(offset, offset + chunkSize);
// await this.uploadChunk(API_URL, chunk, headers);
}
console.log('Upload completed.');
}
async readFileInChunks(filePath: string): Promise<Uint8Array> {
const response = await fetch(filePath);
const reader = response.body.getReader();
const chunks: Uint8Array[] = [];
let done = false;
while (!done) {
const { value, done: isDone } = await reader.read();
if (!isDone) {
chunks.push(value);
}
done = isDone;
}
return new Uint8Array([].concat(...chunks.map((chunk) => Array.from(chunk))));
}
async uploadChunk(url: string, chunks: Uint8Array[], fileName, fileExtension): Promise<void> {
let i = 1
console.log('123', chunks.length)
for(const chunk of chunks) {
try {
console.log("iterate")
const headers = new HttpHeaders()
.append('X-File-Name', fileName)
.append('X-File-Extension', fileExtension)
.append('X-File-Content-Length', chunks.length.toString())
.append('X-File-Index', i.toString())
await this.http.post('http://localhost:3001/upload', chunk.buffer, { headers, responseType: 'blob' }).toPromise();
i++
} catch (error) {
console.error('Upload error:', error);
}
}
}
async uploadChunkNoLoop(url: string, chunk: Uint8Array, fileName, fileExtension, i, length): Promise<void> {
console.log("iterate")
const headers = new HttpHeaders()
.append('X-File-Name', fileName)
.append('X-File-Extension', fileExtension)
.append('X-File-Content-Length', length)
.append('X-File-Index', i.toString())
await this.http.post('http://localhost:3001/upload', chunk.buffer, { headers, responseType: 'blob' }).toPromise();
}
uploadChunk1(chunk: Blob, chunkNumber: number, totalChunks: number, filename: string) {
console.log(chunk)
const headers = new HttpHeaders()
.append('X-File-Name', filename)
.append('X-File-Content-Length', totalChunks.toString())
.append('X-File-Index', chunkNumber.toString())
return this.http.post('http://localhost:3001/upload-chunk', Blob, { headers, responseType: 'blob' });
}
}
// const text = 'Hello, World00120301010asdf1002sdf 0fsdfasf0001230 12300!\n';
@@ -0,0 +1,16 @@
import { TestBed } from '@angular/core/testing';
import { CMAPIAPIService } from './cmapi-api.service';
describe('CMAPIAPIService', () => {
let service: CMAPIAPIService;
beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(CMAPIAPIService);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
});
@@ -0,0 +1,20 @@
import { Injectable } from '@angular/core';
import { HttpServiceService } from 'src/app/services/http/http-service.service';
import { HttpClient, HttpEvent, HttpHeaders, HttpParams } from '@angular/common/http';
import { Observable } from 'rxjs';
import { environment } from 'src/environments/environment';
@Injectable({
providedIn: 'root'
})
export class CMAPIAPIService {
constructor( private HttpServiceService: HttpServiceService,
private http: HttpClient,) { }
getVideoHeader(url: string) {
//return this.http.head('http://localhost:3001/static/'+url, { observe: 'response' })
return this.http.head(environment.apiURL+'ObjectServer/StreamFiles?path='+url, { observe: 'response' })
}
}
@@ -0,0 +1,16 @@
import { TestBed } from '@angular/core/testing';
import { CMAPIService } from './cmapi.service';
describe('CMAPIService', () => {
let service: CMAPIService;
beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(CMAPIService);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
});
+21
View File
@@ -0,0 +1,21 @@
import { Injectable } from '@angular/core';
import { HttpServiceService } from 'src/app/services/http/http-service.service';
import { HttpClient, HttpEvent, HttpHeaders, HttpParams } from '@angular/common/http';
import { Observable } from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class CMAPIService {
constructor( private HttpServiceService: HttpServiceService,
private http: HttpClient,) {
window["CMAPIService"] = this
}
getVideoHeader(url: string): Observable<Boolean> {
return this.http.head(url, { observe: 'response' })
.map(response => response.status === 200)
.catch(() => Observable.of(false));
}
}
@@ -158,7 +158,7 @@ export class MiddlewareServiceService {
// ===========================================================================
CMAPIFileContent({length, path, index, blobFile}) {
CMAPIPing() {
const headers = new HttpHeaders();
headers.set('Authorization', 'Bearer ' + SessionStore.user.Authorization);
@@ -172,13 +172,10 @@ export class MiddlewareServiceService {
const formData = new FormData();
formData.append("blobFile", blobFile);
formData.append("length", length);
formData.append("index", index.toString());
formData.append("blobFile", "blobFile");
formData.append("length", "length");
formData.append("index", "index.toString(");
if(path) {
formData.append("path", path);
}
return this.http.post<IuploadFileLK>(`${geturl}`, formData, options)
}
@@ -197,4 +194,39 @@ export class MiddlewareServiceService {
};
return this.http.get<string>(`${geturl}`, options);
}
CMAPIFileContent({length, path, index, base64}) {
// const geturl = 'http://localhost:3001/FileHub';
const geturl = environment.apiPCURL + 'FileContent/UploadFile';
const data = {
index,
length,
base64,
path,
}
return this.http.post<IuploadFileLK>(`${geturl}`, data)
}
CMAPIRequestUpload() {
const geturl = environment.apiPCURL + 'FileContent/RequestUpload';
return this.http.get<string>(`${geturl}`)
}
CMAPIUploadStatus() {
const geturl = environment.apiPCURL + 'FileContent/UploadStatus';
return this.http.get<string>(`${geturl}`)
}
tryToReachTheServer() {
let opts = {
headers: {},
}
return this.http.post(environment.apiURL + "UserAuthentication/Login", '', opts)
}
}
@@ -53,44 +53,31 @@
X
</div>
<ion-img *ngIf="checkFileType.checkFileType(seleted.FileExtension) == 'image'"
name="image" ngDefaultControl [src]="seleted.FileBase64" style="height: 69px;"></ion-img>
<div *ngIf="checkFileType.checkFileType(seleted.FileExtension) == 'video' && seleted.chucksManager">
<!-- <div class="progress-container" *ngIf="checkFileType.checkFileType(seleted.FileExtension) == 'video'">
<div class="progress-bar" id="myProgressBar"></div>
</div> -->
<!-- <div *ngIf="checkFileType.checkFileType(seleted.FileExtension) == 'video'">
<mat-progress-bar
*ngIf="seleted.chucksManager"
mode="determinate"
style="width: 50%"
[style.width]="seleted.chucksManager.uploadPercentage"
></mat-progress-bar>
</div> -->
<video *ngIf="checkFileType.checkFileType(seleted.FileExtension) == 'video' && checkDesktop() == true" width="70" height="70"
preload="metadata" webkit-playsinline="webkit-playsinline">
<source type="video/mp4" [src]="seleted.FileBase64">
</video>
<video *ngIf="checkFileType.checkFileType(seleted.FileExtension) == 'video' && checkTableDivice() == true" width="70" height="70"
preload="metadata" webkit-playsinline="webkit-playsinline">
<source type="video/mp4" [src]="seleted.FileBase64">
</video>
</div>
</div>
<!-- Display the blurred image and count if there are more images -->
<div *ngIf="seletedContent.length > displayLimit" lot="start">
<ion-img [src]="'data:image/jpg;base64,' + seletedContent[displayLimit - 1].FileBase64"
style="filter: blur(5px);"></ion-img>
<ion-img *ngIf="checkFileType.checkFileType(seleted.FileExtension) == 'image'"
name="image" ngDefaultControl [src]="seleted.url" style="height: 69px;"></ion-img>
<video class="sdf" *ngIf="checkFileType.checkFileType(seleted.FileExtension) == 'video' && checkDesktop() == true" width="70" height="70"
preload="metadata" webkit-playsinline="webkit-playsinline">
<source type="video/mp4" [src]="seleted.url">
</video>
<video class="sdfsdf" *ngIf="checkFileType.checkFileType(seleted.FileExtension) == 'video' && checkTableDivice() == true" width="70" height="70"
preload="metadata" webkit-playsinline="webkit-playsinline">
<source type="video/mp4" [src]="'data:video/mp4;base64,' + seleted.Base64">
</video>
</div>
<p>mais {{ seletedContent.length - displayLimit }}</p>
</div>
<ion-label class="pl-10">
@@ -153,6 +140,10 @@
</div>
</ion-label>
</div>
<video id="yourVideoElementId" >videoss</video>
</div>
</ion-content>
File diff suppressed because one or more lines are too long
@@ -0,0 +1,16 @@
import { TestBed } from '@angular/core/testing';
import { PublicationFormMVService } from './publication-form-mv.service';
describe('PublicationFormMVService', () => {
let service: PublicationFormMVService;
beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(PublicationFormMVService);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
});
@@ -0,0 +1,9 @@
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root'
})
export class PublicationFormMVService {
constructor() { }
}
@@ -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();
});
});
@@ -0,0 +1,512 @@
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"
import { DomSanitizer } from '@angular/platform-browser';
export enum UploadError {
noConnection = 'noConnection',
slow = 'slow'
}
export type IOUploadError = "noConnection" | "slow"
@Injectable({
providedIn: 'root'
})
export class UploadStreamingService {
constructor(
private CMAPIService: CMAPIService,
private sanitizer: DomSanitizer
) {
window["sanitizer"] = this.sanitizer
}
}
class UploadFileUseCase {
CMAPIService: CMAPIService = window["CMAPIAPIRepository"]
constructor() {}
async execute(PublicationAttachmentEntity: PublicationAttachmentEntity): Promise<Result<true, IOUploadError >> {
return new Promise(async (resolve, reject) => {
let path: string;
const length = PublicationAttachmentEntity.chucksManager.chunks.totalChunks.toString()
const readAndUploadChunk = async(index: number) => {
const base64 = await PublicationAttachmentEntity.chucksManager.chunks.getChunks(index)
const uploadRequest = this.CMAPIService.FileContent({length, path: PublicationAttachmentEntity.chucksManager.path, index, base64})
uploadRequest.then((uploadRequest) => {
if(uploadRequest.isOk()) {
PublicationAttachmentEntity.chucksManager.setResponse(index, uploadRequest)
}
})
return uploadRequest;
}
if(!PublicationAttachmentEntity.chucksManager.hasPath()) {
const guidRequest = await this.CMAPIService.RequestUpload()
if(guidRequest.isOk()) {
path = guidRequest.value+".mp4"
PublicationAttachmentEntity.chucksManager.setPath(path)
} else {
const pingRequest = await this.CMAPIService.ping()
if( pingRequest.isErr()) {
return resolve(err(UploadError.noConnection))
} else {
return resolve(err(UploadError.slow))
}
}
}
const allRequest: Promise<any>[] = []
let connection = true
let errorMessage: UploadError.noConnection | UploadError.slow
for (let index = 1; ( (index <= PublicationAttachmentEntity.chucksManager.chunks.totalChunks) && connection ); index++) {
const needUpload = PublicationAttachmentEntity.chucksManager.needToUploadChunkIndex(index)
if(needUpload) {
// upload every chunk at onces
// const request = readAndUploadChunk(index).then(async(uploadRequest) => {
// if(uploadRequest.isErr()) {
// connection = false
// const pingRequest = await this.CMAPIService.ping()
// if( pingRequest.isErr()) {
// errorMessage = UploadError.noConnection
// } else {
// errorMessage = UploadError.slow
// }
// }
// })
// allRequest.push(request)
// one by one chunk upload
const request = readAndUploadChunk(index)
allRequest.push(request)
const uploadRequest = await request
if(uploadRequest.isErr()) {
const pingRequest = await this.CMAPIService.ping()
if( pingRequest.isErr()) {
return resolve(err(UploadError.noConnection))
} else {
return resolve(err(UploadError.slow))
}
}
}
}
await Promise.all(allRequest)
if(!connection) {
return resolve(err(errorMessage))
} else {
return resolve(ok(true))
}
})
}
}
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() {
const sanitizer : DomSanitizer = window["sanitizer"]
if(this.FileType == 'image' ) {
if(!this.Base64.startsWith('data:')) {
this.url = 'data:image/jpg;base64,' + this.Base64
// this.url = sanitizer.bypassSecurityTrustUrl('data:image/jpg;base64,' + this.Base64) as any
} 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
// this.url = sanitizer.bypassSecurityTrustUrl('data:video/mp4;base64,' + this.Base64) as any
} 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()
constructor() {
// this.ObjectMergeNotification.connect();
}
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: 40 })
fileChunks.setFile(fileBlob)
PublicationAttachmentEntity.setChunkManger(fileChunks)
} else if(PublicationAttachmentEntity.chucksManager.doneUpload) {
return resolve(true)
}
let attemp = 0;
let result: Result<true, IOUploadError>
if( PublicationAttachmentEntity.chucksManager.isUploading == false) {
do {
attemp++
PublicationAttachmentEntity.chucksManager.clearManualRetry()
PublicationAttachmentEntity.chucksManager.setUploading()
result = await this.UploadFileUseCase.execute(PublicationAttachmentEntity)
PublicationAttachmentEntity.chucksManager.clearUploading()
} while (attemp<3 && result.isErr() && result.error == 'slow')
if(result.isErr()) {
PublicationAttachmentEntity.chucksManager.setManualRetry()
resolve(false)
} else {
const guid = PublicationAttachmentEntity.chucksManager.path
this.ObjectMergeNotification.subscribe(guid, (data) => {
PublicationAttachmentEntity.chucksManager.contentSetReady()
resolve(true)
})
PublicationAttachmentEntity.chucksManager.doneChunkUpload()
this.ObjectMergeNotification.socket.commit(PublicationAttachmentEntity.chucksManager.path)
resolve(true)
}
} else {
}
})
}
uploadVideosFiles(): Promise<Boolean> {
return new Promise((resolve, reject) => {
// this.ObjectMergeNotification.socket.registerWhenConnected(() => {
const videosFiles = this.getVideoFiles()
const videosFilesToUploads = videosFiles.filter( e => e.FileType == "video")
const Promises: Promise<any>[] = []
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 {
resolve(false)
console.log('Some promises failed to resolve successfully.');
}
})
.catch((error) => {
resolve(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);
}
setFile(file: File) {
this.file = file
}
// Function to read a chunk of the file
readChunk(start: number, end: number): any {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = (event: any) => resolve(event.target.result.split(',')[1]);
reader.onerror = (error) => reject(error);
reader.readAsDataURL(this.file.slice(start, end));
});
}
async getChunks(i: number): Promise<string> {
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<any, Error>
attemp: number
}
export class ChucksManager {
chunks: Chunks
uploads: {[key: string]: IUploadResponse } = {}
path: string = undefined
uploadPercentage: string = "1%"
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.onSetLastChunk.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
}
}
@@ -39,12 +39,12 @@
<p class="item-content-detail">{{publicationFolderService.FolderDetails[folderId].Detail}}</p>
<ion-card *ngFor="let publication of publicationFolderService.publicationList[folderId] let i = index">
<ion-card-content>
<swiper-container [modules]="swiperModules" [speed]=400 navigation="true" [pagination]="{clickable: true, dynamicBullets: true }">
<swiper-slide *ngFor="let files of publication.Files let k = index">
<swiper-container [config]="swiperThumbsConfig" [modules]="swiperModules" [speed]=400 navigation="true" [pagination]="{clickable: true, dynamicBullets: true }">
<swiper-slide *ngFor="let files of publication.Files let k = index" class="centered-slide">
<div class="cool">
<div (click)="viewPublicationDetail(publication.DocumentId, publication.ProcessId)">
<img *ngIf="checkFileType.checkFileType(files.FileExtension ) == 'image'" class="post-img cursor-pointer"
[lazyLoad]="'data:image/jpg;base64,' + files.FileBase64">
@@ -56,6 +56,8 @@
</div>
</div>
</swiper-slide>
<!-- <div *ngIf="publication?.Files?.length == 0">
@@ -292,3 +292,10 @@ swiper-slide video {
align-items: center;
}
.centered-slide {
display: flex;
justify-content: center;
align-items: center;
text-align: center;
}
@@ -29,6 +29,18 @@ export class ViewPublicationsPage implements OnInit {
error: any;
oldpublicationIds = []
swiperConfig = {
spaceBetween: 10,
navigation: true,
}
swiperThumbsConfig = {
spaceBetween: 10,
slidesPerView: 4,
freeMode: true,
watchSlidesProgress: true,
}
@Input() folderId: any;
@Output() addNewPublication = new EventEmitter<any>();
@Output() editPublication = new EventEmitter<any>();
@@ -314,12 +326,11 @@ export class ViewPublicationsPage implements OnInit {
},
});
modal.onDidDismiss().then(async (res) => {
modal.onDidDismiss().then((res) => {
if (res.data == 'Yes') {
const loader = this.toastService.loading();
try {
await this.publications.DeletePresidentialAction(folderId).toPromise();
window["refreshPublication"]();
this.publications.DeletePresidentialAction(folderId).toPromise();
this.httpErrorHandle.httpsSucessMessagge('Eliminar Acção')
} catch (error) {
this.httpErrorHandle.httpStatusHandle(error)
@@ -329,8 +340,6 @@ export class ViewPublicationsPage implements OnInit {
}
this.close();
this.getActions.emit();
}
// Do nothing
});
@@ -370,12 +379,12 @@ export class ViewPublicationsPage implements OnInit {
stopVideo() {
var videos = document.querySelectorAll('video');
try {
// Pause each video
videos.forEach(function (video) {
video.pause();
})
try {
this.videoElements.forEach(videoElement => {
// You can access the native HTML video element using videoElement.nativeElement
@@ -385,10 +394,10 @@ export class ViewPublicationsPage implements OnInit {
// Do something with each video element
// console.log(video);
});
} catch (e) {
} catch (error) {
console.log(error)
}
}
public onScroll(event): void {
@@ -1,25 +1,82 @@
import { Injectable } from '@angular/core';
import { MiddlewareServiceService } from "src/app/shared/API/middleware/middleware-service.service";
import { ok, err } from 'neverthrow';
import { CMAPIAPIService } from "src/app/shared/API/CMAPI/cmapi-api.service";
import { ok, err, Result } from 'neverthrow';
import { Observable } from 'rxjs';
import { environment } from 'src/environments/environment';
import { IuploadFileLK } from '../../API/middleware/interface';
@Injectable({
providedIn: 'root'
})
export class CMAPIService {
constructor(public MiddlewareServiceService: MiddlewareServiceService) {
window["CMAPIService"] = this
constructor(
public MiddlewareServiceService: MiddlewareServiceService,
private CMAPIAPIService: CMAPIAPIService) {
window["CMAPIAPIRepository"] = this
}
async FileContent({length, path, index, blobFile}) {
async FileContent({length, path, index, base64}): Promise<Result<IuploadFileLK, "badRequest" | "other">> {
try {
const result = await this.MiddlewareServiceService.CMAPIFileContent({length, path, index, blobFile}).toPromise();
const result = await this.MiddlewareServiceService.CMAPIFileContent({length, path, index, base64}).toPromise();
return ok(result)
} catch (error) {
if(error.status >= 400 && error.status >= 499) {
return err("badRequest")
}
return err("other")
}
}
async RequestUpload(): Promise<Result<string, any>> {
try {
const result = await this.MiddlewareServiceService.CMAPIRequestUpload().toPromise();
return ok(result)
} catch (error) {
return err(error)
}
}
async UploadStatus() {
try {
const result = await this.MiddlewareServiceService.CMAPIUploadStatus().toPromise();
return ok(result)
} catch (error) {
return err(error)
}
}
async ping() {
try {
await this.MiddlewareServiceService.tryToReachTheServer().toPromise();
return ok(true)
} catch (error) {
if(error.status == 0) {
return err(false)
}
return ok(true)
}
}
async getVideoHeader(url: string) {
try {
await this.CMAPIAPIService.getVideoHeader(url).toPromise()
return ok(true)
} catch(error) {
if(error.status == 405) {
return ok(true)
}
return err(false)
}
}