Change the logo and header colour of nestjs and swagger-ui - swagger-ui

I have installed nestjs and I would like to use swagger-ui. I am not sure what the best way is to change the logo and header.
In main.ts
const options = new DocumentBuilder()
.setTitle('Data Service API')
.setDescription('Data Service API')
.setVersion('1.0')
.addTag('OD')
.build();
const document = SwaggerModule.createDocument(app, options);
SwaggerModule.setup('api', app, document);

I managed to figure out.
const options2 = {
// customCss: '.swagger-ui .topbar { display: none }'
customCss: `
.topbar-wrapper img {content:url(\'../assets/img/lbglogo.png\'); width:300px; height:auto;}
.swagger-ui .topbar { background-color: white; }
`
};
SwaggerModule.setup('api', app, document, options2);
app.useStaticAssets(join(__dirname,'..', 'public'), {prefix: '/assets'});
Reference:
https://www.npmjs.com/package/swagger-ui-express

Related

capture screen with electron when rendering a web site

I have an electron application that loads a web page on the internet.
one of the sites main features is the ability to capture screen, it uses the
navigator.mediaDevices.getDisplayMedia({video: true});
but obviously, the electron will through the Permission denied because there will be no 'selecting window to capture' popped up to grant any permission to it.
I already check out some articles and saw desktopCapture
the problem is, this is happening and running through the web page javascript not my application's code so I don't know how to affect it.
so what should I do to make capturing the screen works in this situation?
You can override navigator.mediaDevices.getDisplayMedia to call Electron's desktopCapturer API like shown below. This implementation assumes you have contextIsolation enabled which is the default behaviour in Electron >= 12
// preload.js
const { desktopCapturer, contextBridge } = require("electron");
const { readFileSync } = require("fs");
const { join } = require("path");
// inject renderer.js into the web page
window.addEventListener("DOMContentLoaded", () => {
const rendererScript = document.createElement("script");
rendererScript.text = readFileSync(join(__dirname, "renderer.js"), "utf8");
document.body.appendChild(rendererScript);
});
contextBridge.exposeInMainWorld("myCustomGetDisplayMedia", async () => {
const sources = await desktopCapturer.getSources({
types: ["window", "screen"],
});
// you should create some kind of UI to prompt the user
// to select the correct source like Google Chrome does
const selectedSource = sources[0]; // this is just for testing purposes
return selectedSource;
});
// renderer.js
navigator.mediaDevices.getDisplayMedia = async () => {
const selectedSource = await globalThis.myCustomGetDisplayMedia();
// create MediaStream
const stream = await navigator.mediaDevices.getUserMedia({
audio: false,
video: {
mandatory: {
chromeMediaSource: "desktop",
chromeMediaSourceId: selectedSource.id,
minWidth: 1280,
maxWidth: 1280,
minHeight: 720,
maxHeight: 720,
},
},
});
return stream;
};
Now when this API is called, a stream will be returned to the caller as expected
navigator.mediaDevices.getDisplayMedia({video: true});
I have created a GitHub repo that has a working implementation of this solution

Is there a way to get Golden Layout pop-outs working in conjunction with Electron windows?

I'm working on a JHipster application that I'm trying to get functioning in Electron. I have Golden Layout for window/pane management and cross-pane communication. I am having several problems with the combination of technologies, including:
I can't pop out more than one pane at the same time into their own Electron windows. I instead get an Uncaught Error: Can't create config, layout not yet initialised error in the console.
Two thirds of the panes don't display anything when popped out into Electron windows, and I'm not sure what the reason is. Any ideas or suggestions for this? One example of content is a leaflet map, another is a "PowerPoint preview" that is really just divs that mock the appearance of slides.
I haven't made it this far yet, but I assume that I will have trouble communicating between popped-out Electron windows when I get more than one open. Right now, the panes communicate between each other using Golden Layout's glEventHub emissions. I have an avenue to explore when I cross that bridge, namely Electron ipcRenderer.
Some borrowed code is here (most of it I can't share because it's company confidential):
electron.js:
const electron = require('electron');
const app = electron.app;
const BrowserWindow = electron.BrowserWindow;
const path = require('path');
const isDev = require('electron-is-dev');
let mainWindow;
function createWindow() {
mainWindow = new BrowserWindow({width: 900, height: 680});
mainWindow.loadURL(isDev ? 'http://localhost:9000' : `file://${path.join(__dirname, '../build/index.html')}`);
if (isDev) {
// Open the DevTools.
//BrowserWindow.addDevToolsExtension('<location to your react chrome extension>');
mainWindow.webContents.openDevTools();
}
mainWindow.on('closed', () => mainWindow = null);
}
app.on('ready', createWindow);
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit();
}
});
app.on('activate', () => {
if (mainWindow === null) {
createWindow();
}
});
goldenLayoutComponent.tsx, a patch for Golden Layout:
import React from "react";
import ReactDOM from "react-dom";
// import "./goldenLayout-dependencies";
import GoldenLayout from "golden-layout";
import "golden-layout/src/css/goldenlayout-base.css";
import "golden-layout/src/css/goldenlayout-dark-theme.css";
import $ from "jquery";
interface IGoldenLayoutProps {
htmlAttrs: {},
config: any,
registerComponents: Function
}
interface IGoldenLayoutState {
renderPanels: Set<any>
}
interface IContainerRef {
current: any
}
export class GoldenLayoutComponent extends React.Component <IGoldenLayoutProps, IGoldenLayoutState> {
goldenLayoutInstance = undefined;
state = {
renderPanels: new Set<any>()
};
containerRef: IContainerRef = React.createRef();
render() {
const panels = Array.from(this.state.renderPanels || []);
return (
<div ref={this.containerRef as any} {...this.props.htmlAttrs}>
{panels.map((panel, index) => {
return ReactDOM.createPortal(
panel._getReactComponent(),
panel._container.getElement()[0]
);
})}
</div>
);
}
componentRender(reactComponentHandler) {
this.setState(state => {
const newRenderPanels = new Set(state.renderPanels);
newRenderPanels.add(reactComponentHandler);
return { renderPanels: newRenderPanels };
});
}
componentDestroy(reactComponentHandler) {
this.setState(state => {
const newRenderPanels = new Set(state.renderPanels);
newRenderPanels.delete(reactComponentHandler);
return { renderPanels: newRenderPanels };
});
}
componentDidMount() {
this.goldenLayoutInstance = new GoldenLayout(
this.props.config || {},
this.containerRef.current
);
if (this.props.registerComponents instanceof Function)
this.props.registerComponents(this.goldenLayoutInstance);
this.goldenLayoutInstance.reactContainer = this;
this.goldenLayoutInstance.init();
}
}
// Patching internal GoldenLayout.__lm.utils.ReactComponentHandler:
const ReactComponentHandler = GoldenLayout["__lm"].utils.ReactComponentHandler;
class ReactComponentHandlerPatched extends ReactComponentHandler {
_container: any;
_reactClass: any;
_render() {
const reactContainer = this._container.layoutManager.reactContainer; // Instance of GoldenLayoutComponent class
if (reactContainer && reactContainer.componentRender)
reactContainer.componentRender(this);
}
_destroy() {
// ReactDOM.unmountComponentAtNode( this._container.getElement()[ 0 ] );
this._container.off("open", this._render, this);
this._container.off("destroy", this._destroy, this);
}
_getReactComponent() {
// the following method is absolute copy of the original, provided to prevent depenency on window.React
const defaultProps = {
glEventHub: this._container.layoutManager.eventHub,
glContainer: this._container
};
const props = $.extend(defaultProps, this._container._config.props);
return React.createElement(this._reactClass, props);
}
}
GoldenLayout["__lm"].utils.ReactComponentHandler = ReactComponentHandlerPatched;
Any help or insight into these issues would be appreciated. Thanks in advance!
If you are still looking for a solutions, 1 and 2 I have solved, if you want to see my solution you could see in this repository.
But it was basically this:
1: The window that popups has a different path than the main window, so I just had to put a try catch in my requires, and you have to set
nativeWindowOpen = true
when creating the Browser window.
2: Solves it's self after 1 I think

What does (Electron) BrowserWindow's baseURLForDataURL option expect?

I'm loading a window from data-uri:
pref.loadURL('data:text/html;charset=utf-8,' + encodeURI(str), { baseURLForDataURL: 'file://' + app.getAppPath() } );
The good news is that the dev tools console shows errors now for the css/js files that should be loading but aren't, but I can't make sense of what it expects. There are no examples anywhere, not even in the github issues that inspired this option.
Does it expect an absolute path (as in my example above)?
It would normally expect
'file://' + app.getAppPath().replace("\\", "/") + "/"
But at the moment, there seem to be an issue where at least when using the protocol 'file://' we get an error. (https://github.com/electron/electron/issues/20700)
One way of going around this issue is by generating a custom file protocol.
const { app, BrowserWindow, screen, protocol } = require('electron');
const path = require('path');
app.on('ready', () => {
const customProtocol = 'file2';
protocol.registerFileProtocol(customProtocol, (request, callback) => {
const url = request.url.substr(customProtocol.length + 2);
const file = { path: path.normalize(`${__dirname}/${url}`) }
callback(file)
});
let win = new BrowserWindow({
webPreferences: {
nodeIntegration: true,
contextIsolation: true
}
});
win.loadURL(`data:text/html;charset=UTF-8,${encodeURIComponent(indexHTML)}`, {
baseURLForDataURL: `${customProtocol}://${app.getAppPath().replace("\\", "/")}/`
});
});

How to get all PDF files from internal as well as external storage in Flutter?

I want to show All pdf files present in internal as well as external storage, So on tapping that particular file, i want to open that file in full screen dialog.
So in order to do that you need to:
Grant access to external storage in a directory where there are your PDF file. Let's call that folder <external storage>/pdf.
List all file of that directory a display them to the user.
Open the selected file with an application that can visualize PDF.
In order to do all that thinks I suggest you to use those flutter packages:
path_provider
simple_permission
With path_provider you can get the external storage directory of an Android device.
Directory extDir = await getExternalStorageDirectory();
String pdfPath = extDir + "/pdf/";
In order to access external storage you need to set this permission request in the ApplicationManifest.xml:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
You could also only use READ_EXTERNAL_STORAGE but then the simple_permission plugin won't work.
With the simple_permission plugin then you go and ask user to be granted external storage access:
bool externalStoragePermissionOkay = false;
_checkPermissions() async {
if (Platform.isAndroid) {
SimplePermissions
.checkPermission(Permission.WriteExternalStorage)
.then((checkOkay) {
if (!checkOkay) {
SimplePermissions
.requestPermission(Permission.WriteExternalStorage)
.then((okDone) {
if (okDone) {
debugPrint("${okDone}");
setState(() {
externalStoragePermissionOkay = okDone;
debugPrint('Refresh UI');
});
}
});
} else {
setState(() {
externalStoragePermissionOkay = checkOkay;
});
}
});
}
}
Once we have been granted external storage access we an list our PDF directory:
List<FileSystemEntity> _files;
_files = dir.listSync(recursive: true, followLinks: false);
And show them in a ListView:
return new ListView.builder(
padding: const EdgeInsets.all(16.0),
itemCount: _files.length,
itemBuilder: (context, i) {
return _buildRow(_files.elementAt(i).path);
});
Than you have to open them with a viewer when the user tap on them.
To do that there isn't an easy way, because with Android we need to build a ContentUri and give access to this URI to the exteranl application viewer.
So we do that in Android and we use flutter platform channels to call the Android native code.
Dart:
static const platform =
const MethodChannel('it.versionestabile.flutterapp000001/pdfViewer');
var args = {'url': fileName};
platform.invokeMethod('viewPdf', args);
Native Java Code:
public class MainActivity extends FlutterActivity {
private static final String CHANNEL = "it.versionestabile.flutterapp000001/pdfViewer";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
GeneratedPluginRegistrant.registerWith(this);
new MethodChannel(getFlutterView(), CHANNEL).setMethodCallHandler(
new MethodChannel.MethodCallHandler() {
#Override
public void onMethodCall(MethodCall call, MethodChannel.Result result) {
if (call.method.equals("viewPdf")) {
if (call.hasArgument("url")) {
String url = call.argument("url");
File file = new File(url);
//*
Uri photoURI = FileProvider.getUriForFile(MainActivity.this,
BuildConfig.APPLICATION_ID + ".provider",
file);
//*/
Intent target = new Intent(Intent.ACTION_VIEW);
target.setDataAndType(photoURI,"application/pdf");
target.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
target.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
startActivity(target);
result.success(null);
}
} else {
result.notImplemented();
}
}
});
}
}
And after all we can have our PDF list and viewable on Android.
You have a lot to study. I hope this could be an useful playground for you.
This is for External Storage, but you can get Also the Internal and Temporary directory and act similarly as here.
If you wanna do the same thing on iOS you need to create the same Native Code pdfViewer also on iOS project. Refer alway to flutter platform channels in order to do it. And remember that the external storage doesn't exists on iOS devices. So you could use only the application sandbox document folder or the temporary one.
GitHub repo.
Happy coding.
i use this code for list files and directories
Future<List<FileSystemEntity>> dirContents(Directory dir) {
var files = <FileSystemEntity>[];
var completer = Completer<List<FileSystemEntity>>();
var lister = dir.list(recursive: false);
lister.listen((file) async {
FileStat f = file.statSync();
if (f.type == FileSystemEntityType.directory) {
await dirContents(Directory(file.uri.toFilePath()));
} else if (f.type == FileSystemEntityType.file && file.path.endsWith('.pdf')) {
_files.add(file);
}
}, onDone: () {
completer.complete(files);
setState(() {
//
});
});
return completer.future;
}
Directory dir = Directory('/storage/emulated/0');
var files = await dirContents(dir);
print(files);
Here is my code to list files from the download folder
List<dynamic> filesList = [];
Future listDir() async {
Directory dir = Directory(
'/storage/emulated/0/Download');
await for (FileSystemEntity entity
in dir.list(recursive: true, followLinks: false)) {
FileSystemEntityType type = await FileSystemEntity.type(entity.path);
if (type == FileSystemEntityType.file &&
entity.path.endsWith('.pdf')) {
filesList.add(entity.path);
}
}
return filesList;}

Azure Media Player on iOS devices

I'm streaming O365 videos using Azure Media Player in a web app that must be used only in mobile devices. It works with WP and Android, but the player stuck on iOS.
This is my code
client.DefaultRequestHeaders.Add("Authorization", "Bearer " + bearer);
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var response = await client.GetAsync($"{url}/GetPlaybackUrl('1')");
var content = await response.Content.ReadAsStringAsync();
var firstVal = JsonConvert.DeserializeObject<VideoToken>(content);
response = await client.GetAsync($"{url}/GetStreamingKeyAccessToken");
content = await response.Content.ReadAsStringAsync();
var secondVal = JsonConvert.DeserializeObject<VideoToken>(content);
Client Side
<video id="newsVideoAMP" class="azuremediaplayer amp-default-skin amp-big-play-centered" tabindex="0"></video>
var initVideoPlayer = function (playbackUrl, streamingKeyAccessToken) {
try {
var myOptions = {
"nativeControlsForTouch": false,
controls: true,
autoplay: false,
techOrder: ["azureHtml5JS", "flashSS", "html5FairPlayHLS", "silverlightSS", "html5"],
logo: { enabled: false }
}
newsVideoPlayer = amp("newsVideoAMP", myOptions,
function () {
this.addEventListener(amp.eventName.error, function () {
window.alert('error');
console.log('Error: amp init');
var errorDetails = newsVideoPlayer.error();
window.alert(errorDetails);
var code = errorDetails.code;
var message = errorDetails.message;
$("#log").append("<li><span>code: " + code + " - detail: " + message + "</span></li>')");
});
});
newsVideoPlayer.src([
{
"src": playbackUrl,
"type": "application/vnd.ms-sstr+xml",
"protectionInfo": [
{
"type": "AES",
"authenticationToken": streamingKeyAccessToken
}
]
}]);
}
catch (err) {
console.log(err);
}
}
I think the issue is related to video encoding. So I tried to use GetPlaybackUrl('0') (and avoid the next token request), but the player stops to work on WP and Android and still not work on iOS.
The logger in callback function doesn't tell me some useful and I have also tried to change the tech order.
Is there a console to manage video encoding in order to avoid the AES algorithm and the decrypt token? Because this doc explain that iOS works with HTML5 tech will no token request. How can I solve? Thanks
I found a workaround to reproduce videos on iOS devices.
Instead of use REST API I put an iframe element in my page with the video embedded.
Like this (using MVC Razor):
var url = "{YourWebsiteUrl}/portals/hub/_layouts/15/VideoEmbedHost.aspx?chId={YourChannelId}&vId={YourVideoId}&width=853&height=480&autoPlay=false&showInfo=false";
<iframe width=853 height=480 id="videoframe" src="#url" allowfullscreen data-spresponsive style='position: absolute; top: 0; left: 0; right: 0; bottom: 0; height: 100%; max-width: 100%;'></iframe>
I get this code from the popup of "embed" menu in the video page of Office365 Video.
If somebody else knows another (and better) method, please let me know. Thanks

Resources