import { Injectable, Inject, ChangeDetectorRef } from '@angular/core'; import { HttpClient, HttpEventType, HttpResponse, HttpRequest } from '@angular/common/http'; import { Observable, Subject } from 'rxjs'; import { map, filter } from 'rxjs/operators'; import { FileProfileSaveRequest, FileProfileSaveResponse, encodeFileProfileSave, decodeFileProfileSave } from '../apis/file-profile-save'; import { FileTalkDownloadRequest, encodeFileTalkDownload, encodeFormDataFileTalkDownload } from '../apis/file-talk-download'; import { FileTalkSaveRequest, FileTalkSaveResponse, encodeFileTalkSave, decodeFileTalkSave } from '../apis/file-talk-save'; import { FileTalkShareRequest, FileTalkShareResponse, encodeFileTalkShare, decodeFileTalkShare } from '../apis/file-talk-share'; import { MassTalkDownloadRequest, MassTalkDownloadResponse, encodeMassTalkDownload, decodeMassTalkDownload } from '../apis/mass-talk-download'; import { MassTalkSaveRequest, MassTalkSaveResponse, encodeMassTalkSave, decodeMassTalkSave } from '../apis/mass-talk-save'; import { TransMassTalkDownloadRequest, TransMassTalkDownloadResponse, encodeTransMassTalkDownload, decodeTransMassTalkDownload } from '../apis/trans-mass-talk-download'; import { TransMassTalkSaveRequest, TransMassTalkSaveResponse, encodeTransMassTalkSave, decodeTransMassTalkSave } from '../apis/trans-mass-talk-save'; import { TranslationReqRequest, TranslationReqResponse, encodeTranslationReq, decodeTranslationReq } from '../apis/translation-req'; import { TranslationSaveRequest, TranslationSaveResponse, encodeTranslationSave, decodeTranslationSave } from '../apis/translation-save'; import { _MODULE_CONFIG } from '../config/token'; import { ModuleConfig } from '../config/module-config'; import { Urls } from '../config/urls'; import { UrlConfig, MimeUtil, FileUtil } from '@ucap-webmessenger/core'; @Injectable({ providedIn: 'root' }) export class CommonApiService { readonly urls: Urls; constructor( @Inject(_MODULE_CONFIG) private moduleConfig: ModuleConfig, private httpClient: HttpClient ) { this.urls = UrlConfig.getUrls( this.moduleConfig.hostConfig, this.moduleConfig.urls ); } public fileProfileSave( req: FileProfileSaveRequest, fileProfileSaveUrl?: string ): Observable { const httpReq = new HttpRequest( 'POST', !!fileProfileSaveUrl ? fileProfileSaveUrl : this.urls.fileProfileSave, encodeFileProfileSave(req), { reportProgress: true, responseType: 'text' as 'json' } ); const progress = req.fileUploadItem.uploadStart(); return this.httpClient.request(httpReq).pipe( filter(event => { if (event instanceof HttpResponse) { return true; } else if (HttpEventType.UploadProgress === event.type) { progress.next(Math.round((100 * event.loaded) / event.total)); } return false; }), map((event: HttpResponse) => { req.fileUploadItem.uploadComplete(); return decodeFileProfileSave(event.body); }) ); } public urlForFileTalkDownload( req: FileTalkDownloadRequest, fileTalkDownloadUrl?: string ): string { const httpReq = new HttpRequest( 'GET', !!fileTalkDownloadUrl ? fileTalkDownloadUrl : this.urls.fileTalkDownload, {}, { params: encodeFileTalkDownload(req) } ); return httpReq.urlWithParams; } public fileTalkDownload( req: FileTalkDownloadRequest, fileTalkDownloadUrl?: string ): Observable { const httpReq = new HttpRequest( 'POST', !!fileTalkDownloadUrl ? fileTalkDownloadUrl : this.urls.fileTalkDownload, encodeFormDataFileTalkDownload(req), { reportProgress: true, responseType: 'blob' } ); let progress: Subject; if (!!req.fileDownloadItem) { progress = req.fileDownloadItem.downloadStart(); } return this.httpClient.request(httpReq).pipe( filter(event => { if (event instanceof HttpResponse) { return true; } else if (HttpEventType.DownloadProgress === event.type) { if (!!progress) { progress.next(Math.round((100 * event.loaded) / event.total)); } } return false; }), map((event: HttpResponse) => { if (!!progress) { req.fileDownloadItem.downloadComplete(); } return event.body; }) ); } public fileTalkSave( req: FileTalkSaveRequest, fileTalkSaveUrl?: string ): Observable { const httpReq = new HttpRequest( 'POST', !!fileTalkSaveUrl ? fileTalkSaveUrl : this.urls.fileTalkSave, encodeFileTalkSave(req), { reportProgress: true, responseType: 'text' as 'json' } ); const progress = req.fileUploadItem.uploadStart(); return this.httpClient.request(httpReq).pipe( filter(event => { if (event instanceof HttpResponse) { return true; } else if (HttpEventType.UploadProgress === event.type) { progress.next(Math.round((100 * event.loaded) / event.total)); } return false; }), map((event: HttpResponse) => { req.fileUploadItem.uploadComplete(); return decodeFileTalkSave(event.body); }) ); } public acceptableExtensionForFileTalk(extensions: string[]): string[] { const rejected: string[] = []; for (const extension of extensions) { if ( !extension || -1 === this.moduleConfig.acceptableFileExtensions.indexOf( extension.toLowerCase() ) ) { rejected.push(!!extension ? extension : 'empty-ext'); } } return 0 === rejected.length ? undefined : rejected; } public checkInvalidMediaMimeForFileTalk(files: File[]): Promise { return new Promise(async (resolve, reject) => { const mediaFiles = this.mediaFiles(files); if (!mediaFiles) { resolve(undefined); return; } const rejected: string[] = []; for (const file of mediaFiles) { const info = await MimeUtil.getMimeFromBuffer( await FileUtil.fromBlobToBuffer(file) ); if ( !file || !info || (-1 === info.mime.indexOf('video/') && -1 === info.mime.indexOf('image/')) ) { rejected.push(file.name); } } resolve(0 === rejected.length ? undefined : rejected); return; }); } mediaFiles(files: File[]): File[] { const filtered: File[] = []; for (const file of files) { const extension = FileUtil.getExtension(file.name).toLocaleLowerCase(); if ( !!file && -1 !== this.moduleConfig.acceptableFileExtensionsForImage.indexOf(extension) ) { filtered.push(file); } if ( !!file && -1 !== this.moduleConfig.acceptableFileExtensionsForVideo.indexOf(extension) ) { filtered.push(file); } } return 0 < filtered.length ? filtered : undefined; } /** @deprecated */ public mimeCheckForImageAndVideoFiles(files: File[]) { files.forEach(file => { console.log(file); const fileReader = new FileReader(); fileReader.onloadend = () => { const arr = new Uint8Array(fileReader.result as ArrayBuffer).subarray( 0, 4 ); let header = ''; for (let i = 0; i < arr.length; i++) { header += arr[i].toString(16); } console.log('File header: ' + header); // Check the file signature against known types var type = 'unknown'; // http://forensic-proof.com/archives/300?ckattempt=1 if (header.startsWith('89504e47')) { // 89504E470D0A1A0A type = 'image/png'; } else if (header.startsWith('47494638')) { // 474946383761 // 474946383961 type = 'image/gif'; } else if (header.startsWith('ffd8ffe')) { // ffd8ffe0 // ffd8ffe1 // ffd8ffe2 // ffd8ffe8 type = 'image/jpeg'; } else if (header.startsWith('424d')) { type = 'image/bmp'; } else if (header.startsWith('4d4d') || header.startsWith('4949')) { type = 'image/tiff'; } else if (header.startsWith('4F676753')) { // 4F67675300020000 type = 'image/ogg'; } else if (header.startsWith('38425053')) { // 38 42 50 53 type = 'image/psd'; } else if (header.startsWith('25504446')) { // 38 42 50 53 type = 'image/ai'; } else if (header.startsWith('52494646')) { // avi type = 'video/x-msvideo'; } else if (header.startsWith('00000018')) { // 0000001866747970 type = 'video/x-msvideo'; } console.log('type', type); }; fileReader.readAsArrayBuffer(file); }); } public fileTalkShare( req: FileTalkShareRequest ): Observable { return this.httpClient .post( this.urls.fileTalkShare, {}, { params: encodeFileTalkShare(req) } ) .pipe(map(res => decodeFileTalkShare(res))); } public massTalkDownload( req: MassTalkDownloadRequest ): Observable { return this.httpClient .post( this.urls.massTalkDownload, {}, { params: encodeMassTalkDownload(req), responseType: 'text' as 'json' } ) .pipe(map(res => decodeMassTalkDownload(res))); } public massTalkSave( req: MassTalkSaveRequest ): Observable { const httpReq = new HttpRequest( 'POST', this.urls.massTalkSave, encodeMassTalkSave(req), { reportProgress: true, responseType: 'text' as 'json' } ); return this.httpClient.request(httpReq).pipe( filter(event => { if (event instanceof HttpResponse) { return true; } return false; }), map(res => decodeMassTalkSave((res as HttpResponse).body)) ); } public transMassTalkDownload( req: TransMassTalkDownloadRequest ): Observable { return this.httpClient .post( this.urls.transMassTalkDownload, {}, { params: encodeTransMassTalkDownload(req) } ) .pipe(map(res => decodeTransMassTalkDownload(res))); } public transMassTalkSave( req: TransMassTalkSaveRequest ): Observable { return this.httpClient .post( this.urls.transMassTalkSave, {}, { params: encodeTransMassTalkSave(req) } ) .pipe(map(res => decodeTransMassTalkSave(res))); } public translationReq( req: TranslationReqRequest ): Observable { return this.httpClient .post( this.urls.translationReq, {}, { params: encodeTranslationReq(req) } ) .pipe(map(res => decodeTranslationReq(res))); } public translationSave( req: TranslationSaveRequest ): Observable { const httpReq = new HttpRequest( 'POST', this.urls.translationSave, encodeTranslationSave(req), { reportProgress: true } ); return this.httpClient.request(httpReq).pipe( filter(event => { if (event instanceof HttpResponse) { return true; } return false; }), map(res => decodeTranslationSave((res as HttpResponse).body)) ); } }