diff --git a/package-lock.json b/package-lock.json
index c8e23879..9e703ed1 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -11,6 +11,7 @@
"dependencies": {
"@dead50f7/adbkit": "^2.11.4",
"express": "^4.21.2",
+ "express-basic-auth": "^1.2.1",
"ios-device-lib": "^0.9.2",
"node-mjpeg-proxy": "^0.3.2",
"node-pty": "^0.10.1",
@@ -2519,7 +2520,6 @@
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz",
"integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==",
- "optional": true,
"dependencies": {
"safe-buffer": "5.1.2"
},
@@ -2530,8 +2530,7 @@
"node_modules/basic-auth/node_modules/safe-buffer": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
- "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
- "optional": true
+ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
},
"node_modules/basic-ftp": {
"version": "5.0.5",
@@ -4116,6 +4115,15 @@
"url": "https://opencollective.com/express"
}
},
+ "node_modules/express-basic-auth": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/express-basic-auth/-/express-basic-auth-1.2.1.tgz",
+ "integrity": "sha512-L6YQ1wQ/mNjVLAmK3AG1RK6VkokA1BIY6wmiH304Xtt/cLTps40EusZsU1Uop+v9lTDPxdtzbFmdXfFO3KEnwA==",
+ "license": "MIT",
+ "dependencies": {
+ "basic-auth": "^2.0.1"
+ }
+ },
"node_modules/express/node_modules/debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
diff --git a/package.json b/package.json
index cc92a219..dd4339ea 100644
--- a/package.json
+++ b/package.json
@@ -8,6 +8,7 @@
"dist:prod": "webpack --config webpack/ws-scrcpy.prod.ts --stats-error-details",
"dist": "npm run dist:prod",
"start": "npm run dist && cd dist && npm start",
+ "dev": "npm run dist:dev && cd dist && npm start",
"script:dist:start": "node ./index.js",
"lint": "eslint src/ --ext .ts",
"format": "eslint src/ --fix --ext .ts",
@@ -18,6 +19,7 @@
"dependencies": {
"@dead50f7/adbkit": "^2.11.4",
"express": "^4.21.2",
+ "express-basic-auth": "^1.2.1",
"ios-device-lib": "^0.9.2",
"node-mjpeg-proxy": "^0.3.2",
"node-pty": "^0.10.1",
diff --git a/src/app/DisplayInfo.ts b/src/app/DisplayInfo.ts
index ea3c0114..3d16ae5e 100644
--- a/src/app/DisplayInfo.ts
+++ b/src/app/DisplayInfo.ts
@@ -16,7 +16,15 @@ export class DisplayInfo {
public readonly rotation: number,
public readonly layerStack: number,
public readonly flags: number,
- ) {}
+ ) {
+ const deviceViewElem = document.getElementsByClassName('device-view')[0] as HTMLElement;
+ if( rotation && deviceViewElem ){
+ deviceViewElem.style.maxWidth = "none";
+ }
+ else if( deviceViewElem ){
+ deviceViewElem.style.maxWidth = "460px";
+ }
+ }
public toBuffer(): Buffer {
const temp = Buffer.alloc(DisplayInfo.BUFFER_LENGTH);
diff --git a/src/app/client/BaseDeviceTracker.ts b/src/app/client/BaseDeviceTracker.ts
index 1e045ae4..c97f65a1 100644
--- a/src/app/client/BaseDeviceTracker.ts
+++ b/src/app/client/BaseDeviceTracker.ts
@@ -105,13 +105,19 @@ export abstract class BaseDeviceTracker
{
+ // console.log(TAG, 'Building row (after initiating refresh)', item);
this.buildDeviceRow(block, item);
});
+
}
private setNameValue(parent: Element | null, name: string): void {
diff --git a/src/app/client/StreamReceiver.ts b/src/app/client/StreamReceiver.ts
index 0705543a..bf99df19 100644
--- a/src/app/client/StreamReceiver.ts
+++ b/src/app/client/StreamReceiver.ts
@@ -25,6 +25,7 @@ export type DisplayCombinedInfo = {
interface StreamReceiverEvents {
video: ArrayBuffer;
deviceMessage: DeviceMessage;
+ rotated: any;
displayInfo: DisplayCombinedInfo[];
clientsStats: ClientsStats;
encoders: string[];
@@ -67,6 +68,7 @@ export class StreamReceiver extends ManagerClient
diff --git a/src/app/googDevice/client/StreamClientScrcpy.ts b/src/app/googDevice/client/StreamClientScrcpy.ts
index 29e8f9ca..110d656b 100644
--- a/src/app/googDevice/client/StreamClientScrcpy.ts
+++ b/src/app/googDevice/client/StreamClientScrcpy.ts
@@ -30,6 +30,7 @@ import { ACTION } from '../../../common/Action';
import { StreamReceiverScrcpy } from './StreamReceiverScrcpy';
import { ParamsDeviceTracker } from '../../../types/ParamsDeviceTracker';
import { ScrcpyFilePushStream } from '../filePush/ScrcpyFilePushStream';
+import genericAndroid from "../../../common/generic_android.png"
type StartParams = {
udid: string;
@@ -320,6 +321,11 @@ export class StreamClientScrcpy
deviceView.appendChild(this.controlButtons);
const video = document.createElement('div');
video.className = 'video';
+ const genericAndroidMockup = document.createElement("img");
+ genericAndroidMockup.src = genericAndroid;
+ genericAndroidMockup.id = "generic-android-mockup";
+ genericAndroidMockup.style.height = "";
+ video.appendChild(genericAndroidMockup);
deviceView.appendChild(video);
deviceView.appendChild(moreBox);
player.setParent(video);
@@ -340,6 +346,7 @@ export class StreamClientScrcpy
const streamReceiver = this.streamReceiver;
streamReceiver.on('deviceMessage', this.OnDeviceMessage);
+ streamReceiver.on('rotated', ()=>{ this.player?.reOrientScreen(true, this.player) });
streamReceiver.on('video', this.onVideo);
streamReceiver.on('clientsStats', this.onClientsStats);
streamReceiver.on('displayInfo', this.onDisplayInfo);
diff --git a/src/app/googDevice/toolbox/GoogToolBox.ts b/src/app/googDevice/toolbox/GoogToolBox.ts
index 83aeb8dd..78be73dc 100644
--- a/src/app/googDevice/toolbox/GoogToolBox.ts
+++ b/src/app/googDevice/toolbox/GoogToolBox.ts
@@ -101,7 +101,7 @@ export class GoogToolBox extends ToolBox {
const element = el.getElement();
moreBox.style.display = element.checked ? 'block' : 'none';
});
- elements.unshift(more);
+ // elements.unshift(more);
}
return new GoogToolBox(elements);
}
diff --git a/src/app/index.ts b/src/app/index.ts
index cbc7a281..32d04fd8 100644
--- a/src/app/index.ts
+++ b/src/app/index.ts
@@ -3,9 +3,24 @@ import { StreamClientScrcpy } from './googDevice/client/StreamClientScrcpy';
import { HostTracker } from './client/HostTracker';
import { Tool } from './client/Tool';
+
window.onload = async function (): Promise {
+ // Redirect if the URL contains a hash fragment
const hash = location.hash.replace(/^#!/, '');
- const parsedQuery = new URLSearchParams(hash);
+ if (hash) {
+
+ const parsedQuery = new URLSearchParams(hash);
+
+ // Add `hasHash=true` to the query parameters
+ parsedQuery.set('hasHash', 'true');
+
+ // Redirect to a query-based URL
+ window.location.href = `/?${parsedQuery.toString()}`;
+ return; // Stop further execution
+ }
+
+ // If no hash fragment, proceed with the rest of the logic
+ const parsedQuery = new URLSearchParams(location.search); // Use query parameters instead of hash
const action = parsedQuery.get('action');
/// #if USE_BROADWAY
diff --git a/src/app/player/BasePlayer.ts b/src/app/player/BasePlayer.ts
index ab881ce5..5f1f3ca8 100644
--- a/src/app/player/BasePlayer.ts
+++ b/src/app/player/BasePlayer.ts
@@ -46,7 +46,7 @@ export interface PlayerClass {
fitToScreen: boolean,
displayInfo?: DisplayInfo,
): void;
- new (udid: string, displayInfo?: DisplayInfo): BasePlayer;
+ new(udid: string, displayInfo?: DisplayInfo): BasePlayer;
}
export abstract class BasePlayer extends TypedEmitter {
@@ -113,6 +113,39 @@ export abstract class BasePlayer extends TypedEmitter {
super();
this.touchableCanvas = document.createElement('canvas');
this.touchableCanvas.className = 'touch-layer';
+ this.touchableCanvas.style.width = "calc(100vw - 3rem)";
+ if (window.innerWidth > 380)
+ this.touchableCanvas.style.maxWidth = "315px";
+ else
+ this.touchableCanvas.style.maxWidth = "80vw";
+
+
+ const myInterval = setInterval(() => {
+ if (tag.clientHeight || tag.clientWidth) {
+ this.reOrientScreen();
+
+ window.addEventListener('resize', () => {
+ this.reOrientScreen();
+ });
+
+ window.addEventListener('message', (e) => {
+ const allowedOrigins = [
+ "https://trust-me-bro.nativebridge.io",
+ "http://localhost:5173",
+ ];
+
+ if (!allowedOrigins.includes(e.origin)) {
+ console.warn("Blocked message from untrusted origin:", e.origin);
+ return; // Reject messages from untrusted origins
+ }
+ if(e.data.event === "screenshot"){
+ window.top?.postMessage({ event: "screenshot", imageUrl: this.getImageDataURL() }, "*"); // Replace '*' with the specific origin for security
+ }
+ });
+
+ clearInterval(myInterval);
+ }
+ }, 500);
this.touchableCanvas.oncontextmenu = function (event: MouseEvent): void {
event.preventDefault();
};
@@ -120,6 +153,211 @@ export abstract class BasePlayer extends TypedEmitter {
this.videoSettings = BasePlayer.getVideoSettingFromStorage(preferred, this.storageKeyPrefix, udid, displayInfo);
}
+ protected sendDataToParent(rotation: number | undefined): void {
+ // Send data to the parent window
+
+ const videoElemParent = document.getElementsByClassName("video")[0] as HTMLElement;
+ const videoElem = document.getElementsByClassName("video-layer")[0] as HTMLElement;
+ const touchElem = document.getElementsByClassName("touch-layer")[0] as HTMLElement;
+
+ const remToPx = (rem: number) => rem * parseFloat(getComputedStyle(document.documentElement).fontSize);
+
+ const androidFrame = document.getElementById("generic-android-mockup");
+
+ if (androidFrame) {
+ // androidFrame.style.width = "calc(100vw - 3rem)";
+
+
+ const offset = remToPx(3); // Convert 3rem to pixels
+ let initialWidth;
+
+ if( rotation ){
+ if( window.innerWidth <= 380 )
+ initialWidth = window.innerWidth - offset;
+ else
+ initialWidth = window.innerWidth - offset > 940 ? 940 : window.innerWidth - offset
+ }
+ else {
+
+ if( window.innerWidth <= 380 )
+ initialWidth = window.innerWidth - offset;
+ else
+ initialWidth = window.innerWidth - offset > 420 ? 420 : window.innerWidth - offset
+ }
+ // const initialWidth = window.innerWidth <= 380 ? window.innerWidth - offset : ;
+ // androidFrame.style.transformOrigin = Math.abs((initialWidth) / 2) + "px " + Math.abs((initialWidth) / 2) + "px";
+
+ if (window.innerWidth > 380){
+
+ if( rotation ){
+
+ androidFrame.style.transform = "rotateZ(-90deg)";
+ androidFrame.style.transformOrigin = Math.abs((initialWidth * ( 191/320 )) / 2) + "px " + Math.abs((initialWidth * ( 191/320 )) / 2) + "px";
+ androidFrame.style.width = "auto";
+ videoElemParent.style.width = initialWidth + "px";
+ androidFrame.style.height = initialWidth + "px";
+ androidFrame.style.aspectRatio = "191/320";
+ // androidFrame.style.scale = "1.07";
+ androidFrame.style.marginTop = "0px";
+ androidFrame.style.marginLeft = "0px";
+ }
+ else {
+
+ androidFrame.style.transform = "";
+ androidFrame.style.transformOrigin = Math.abs((initialWidth) / 2) + "px " + Math.abs((initialWidth) / 2) + "px";
+ androidFrame.style.width = initialWidth + "px";
+ androidFrame.style.height = "auto";
+ androidFrame.style.aspectRatio = "47/80";
+ // androidFrame.style.scale = "1.07";
+ androidFrame.style.marginTop = "0px";
+ androidFrame.style.marginLeft = "0px";
+ }
+
+ }
+ else{
+
+ if (rotation) {
+ androidFrame.style.maxWidth = "90vw";
+
+ console.log("initialWidth ", initialWidth);
+ androidFrame.style.transform = "rotateZ(-90deg)";
+ androidFrame.style.transformOrigin = Math.abs(((initialWidth)*( 37 / 64 )) / 2) + "px " + Math.abs(((initialWidth)*( 37 / 64 )) / 2) + "px";
+ androidFrame.style.width = "auto";
+ androidFrame.style.height = "calc(100vw - 3rem)";
+ androidFrame.style.aspectRatio = "38/64";
+ androidFrame.style.scale = "1";
+ androidFrame.style.marginTop = "5px";
+ androidFrame.style.marginLeft = "0px";
+
+ }
+ else {
+ androidFrame.style.transform = "";
+ androidFrame.style.transformOrigin = Math.abs((initialWidth) / 2) + "px " + Math.abs((initialWidth) / 2) + "px";
+ androidFrame.style.width = initialWidth + "px";
+ androidFrame.style.aspectRatio = "95/161" //"151/256";
+ // androidFrame.style.scale = "1.01";
+ androidFrame.style.marginTop = "3px";
+ androidFrame.style.marginLeft = "0px";
+ }
+ }
+ }
+
+ if (videoElem) {
+ if (rotation) {
+
+ if (window.innerWidth > 380){
+ videoElem.style.width = "calc(100vw - 4.5rem)";
+ videoElem.style.maxWidth = "910px";
+ videoElem.style.borderRadius = "1.5rem";
+ videoElem.style.marginTop = "3.4%";
+ videoElem.style.marginLeft = "11px";
+ }
+ else{
+
+ videoElem.style.width = "calc(100vw - 4rem)";
+ videoElem.style.maxWidth = "84vw";
+ videoElem.style.borderRadius = "1rem";
+ videoElem.style.marginTop = "14px";
+ videoElem.style.marginLeft = "15px";
+ }
+ }
+ else {
+
+ videoElem.style.width = "calc(100vw - 3rem)";
+
+ if (window.innerWidth > 380){
+
+ videoElem.style.width = "calc(100vw - 5.5rem)";
+ videoElem.style.maxWidth = "380px";
+ videoElem.style.marginTop = "4%";
+ videoElem.style.marginLeft = "4%";
+ }
+ else{
+
+ videoElem.style.width = "calc(100vw - 4rem)";
+ videoElem.style.maxWidth = "80vw";
+ videoElem.style.marginTop = "4%";
+ videoElem.style.marginLeft = "4.5%";
+ }
+ }
+ }
+ if (touchElem) {
+ if (rotation) {
+
+ if (window.innerWidth > 380){
+ touchElem.style.width = "calc(100vw - 4.5rem)";
+ touchElem.style.maxWidth = "910px";
+ touchElem.style.borderRadius = "1.5rem";
+ touchElem.style.marginTop = "3.4%";
+ touchElem.style.marginLeft = "11px";
+ }
+ else{
+
+ touchElem.style.width = "calc(100vw - 4rem)";
+ touchElem.style.maxWidth = "84vw";
+ touchElem.style.borderRadius = "1rem";
+ touchElem.style.marginTop = "11px";
+ touchElem.style.marginLeft = "7px";
+ }
+ }
+ else {
+
+ if (window.innerWidth > 380){
+ touchElem.style.width = "calc(100vw - 5.5rem)";
+ touchElem.style.maxWidth = "380px";
+ touchElem.style.marginTop = "4%";
+ touchElem.style.marginLeft = "4%";
+ }
+ else{
+
+ touchElem.style.width = "calc(100vw - 4rem)";
+ touchElem.style.maxWidth = "80vw";
+ touchElem.style.marginTop = "4%";
+ touchElem.style.marginLeft = "4.5%";
+ }
+ }
+ }
+
+ window.top?.postMessage({ event: "device-rotation", rotation: rotation }, "*"); // Replace '*' with the specific origin for security
+ }
+
+ public reOrientScreen(invert: boolean = false, player: BasePlayer = this): void {
+
+ // if( !player ){
+ // console.log("player not found");
+ // player = this;
+ // }
+
+ // else {
+ // console.log("player found");
+ // }
+
+ // const deviceMockup = document.getElementsByClassName("generic-android-mockup")[0] as HTMLElement;
+ // console.log("invert ", invert, player.displayInfo?.rotation, player.videoWidth, player.videoHeight);
+ let rotation = invert ? !player?.displayInfo?.rotation : player?.displayInfo?.rotation as number | boolean;
+
+ this.sendDataToParent(player?.displayInfo?.rotation);
+
+ if (rotation) {
+
+ // deviceMockup.style.height = player.videoWidth + "px";
+ // deviceMockup.style.width = (player.videoHeight + 23) + "px";
+ // player.touchableCanvas.style.width = player.videoWidth + "px";
+ // player.touchableCanvas.style.height = (player.videoHeight) + "px";
+ // deviceMockup.style.transform = "translate(54%, -26.5%) rotateZ(90deg)";
+ }
+ else {
+ // deviceMockup.style.width = player.videoWidth + "px";
+ // deviceMockup.style.height = (player.videoHeight + 23) + "px";
+ // player.touchableCanvas.style.width = player.videoWidth + "px";
+ // player.touchableCanvas.style.height = (player.videoHeight) + "px";
+ // deviceMockup.style.transform = "none";
+ }
+
+ player.touchableCanvas.style.zIndex = "20";
+ }
+
+
protected calculateScreenInfoForBounds(videoWidth: number, videoHeight: number): void {
this.videoWidth = videoWidth;
this.videoHeight = videoHeight;
diff --git a/src/app/player/MsePlayer.ts b/src/app/player/MsePlayer.ts
index 3f645ed5..ffcd3e07 100644
--- a/src/app/player/MsePlayer.ts
+++ b/src/app/player/MsePlayer.ts
@@ -40,6 +40,8 @@ export class MsePlayer extends BasePlayer {
tag.id = id;
}
tag.className = 'video-layer';
+ tag.style.width = "calc(100vw - 3rem)";
+ tag.style.maxWidth = "315px";
return tag;
}
diff --git a/src/common/generic_android.png b/src/common/generic_android.png
new file mode 100644
index 00000000..c8789769
Binary files /dev/null and b/src/common/generic_android.png differ
diff --git a/src/public/index.html b/src/public/index.html
index 394398c4..74ac2d38 100644
--- a/src/public/index.html
+++ b/src/public/index.html
@@ -4,7 +4,56 @@
WS scrcpy
+
+
+
-