mirror of
https://github.com/schlagmichdoch/PairDrop.git
synced 2026-04-23 07:30:54 +08:00
Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| fea15d3ee1 | |||
| 028752a809 | |||
| 1093f4d246 | |||
| 7ddd600b0c | |||
| 715356aafb | |||
| 490e4db380 | |||
| 11a988e550 | |||
| ff8f28660a |
@@ -6,7 +6,7 @@
|
|||||||
<h1>PairDrop</h1>
|
<h1>PairDrop</h1>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
Local file sharing in your browser. Inspired by Apple's Airdrop.
|
Local file sharing in your browser. Inspired by Apple's AirDrop.
|
||||||
<br />
|
<br />
|
||||||
<a href="https://pairdrop.net"><strong>Explore »</strong></a>
|
<a href="https://pairdrop.net"><strong>Explore »</strong></a>
|
||||||
<br />
|
<br />
|
||||||
|
|||||||
+5
-2
@@ -58,8 +58,11 @@ If your devices are paired and behind a NAT, the public TURN Server from [Open R
|
|||||||
Yes. Your files are sent using WebRTC, which encrypts them on transit. To ensure the connection is secure and there is no MITM, compare the security number shown under the device name on both devices. The security number is different for every connection.
|
Yes. Your files are sent using WebRTC, which encrypts them on transit. To ensure the connection is secure and there is no MITM, compare the security number shown under the device name on both devices. The security number is different for every connection.
|
||||||
|
|
||||||
### Transferring many files with paired devices takes too long
|
### Transferring many files with paired devices takes too long
|
||||||
Naturally, if traffic needs to be routed through the turn server transfer speed decreases.
|
Naturally, if traffic needs to be routed through the turn server because your devices are behind different NATs, transfer speed decreases.
|
||||||
As a workaround you can open a hotspot on one of your devices to bridge the connection which makes transfers much faster.
|
|
||||||
|
As the public TURN server used is not super fast, you can easily [specify to use your own TURN server](https://github.com/schlagmichdoch/PairDrop/blob/master/docs/host-your-own.md#specify-stunturn-servers) if you host your own instance.
|
||||||
|
|
||||||
|
Alternatively, you can open a hotspot on one of your devices to bridge the connection which makes transfers much faster as no TURN server is needed.
|
||||||
|
|
||||||
- [How to open a hotspot on Windows](https://support.microsoft.com/en-us/windows/use-your-windows-pc-as-a-mobile-hotspot-c89b0fad-72d5-41e8-f7ea-406ad9036b85#WindowsVersion=Windows_11)
|
- [How to open a hotspot on Windows](https://support.microsoft.com/en-us/windows/use-your-windows-pc-as-a-mobile-hotspot-c89b0fad-72d5-41e8-f7ea-406ad9036b85#WindowsVersion=Windows_11)
|
||||||
- [How to open a hotspot on Mac](https://support.apple.com/guide/mac-help/share-internet-connection-mac-network-users-mchlp1540/mac)
|
- [How to open a hotspot on Mac](https://support.apple.com/guide/mac-help/share-internet-connection-mac-network-users-mchlp1540/mac)
|
||||||
|
|||||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "pairdrop",
|
"name": "pairdrop",
|
||||||
"version": "1.4.3",
|
"version": "1.4.5",
|
||||||
"lockfileVersion": 2,
|
"lockfileVersion": 2,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "pairdrop",
|
"name": "pairdrop",
|
||||||
"version": "1.4.3",
|
"version": "1.4.5",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"express": "^4.18.2",
|
"express": "^4.18.2",
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "pairdrop",
|
"name": "pairdrop",
|
||||||
"version": "1.4.3",
|
"version": "1.4.5",
|
||||||
"description": "",
|
"description": "",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
+3
-3
@@ -122,7 +122,7 @@
|
|||||||
<div class="font-subheading center text-center">Enter key from another device to continue.</div>
|
<div class="font-subheading center text-center">Enter key from another device to continue.</div>
|
||||||
<div class="center row-reverse">
|
<div class="center row-reverse">
|
||||||
<button class="button" type="submit" disabled>Pair</button>
|
<button class="button" type="submit" disabled>Pair</button>
|
||||||
<button class="button" close>Cancel</button>
|
<button class="button" type="button" close>Cancel</button>
|
||||||
</div>
|
</div>
|
||||||
</x-paper>
|
</x-paper>
|
||||||
</x-background>
|
</x-background>
|
||||||
@@ -137,7 +137,7 @@
|
|||||||
<div class="font-subheading center text-center">Are you sure to unpair all devices?</div>
|
<div class="font-subheading center text-center">Are you sure to unpair all devices?</div>
|
||||||
<div class="center row-reverse">
|
<div class="center row-reverse">
|
||||||
<button class="button" type="submit">Unpair Devices</button>
|
<button class="button" type="submit">Unpair Devices</button>
|
||||||
<button class="button" close>Cancel</button>
|
<button class="button" type="button" close>Cancel</button>
|
||||||
</div>
|
</div>
|
||||||
</x-paper>
|
</x-paper>
|
||||||
</x-background>
|
</x-background>
|
||||||
@@ -209,7 +209,7 @@
|
|||||||
<div id="text-input" class="textarea" role="textbox" autocapitalize="none" spellcheck="false" autofocus contenteditable></div>
|
<div id="text-input" class="textarea" role="textbox" autocapitalize="none" spellcheck="false" autofocus contenteditable></div>
|
||||||
<div class="center row-reverse">
|
<div class="center row-reverse">
|
||||||
<button class="button" type="submit" title="STR + ENTER" disabled close>Send</button>
|
<button class="button" type="submit" title="STR + ENTER" disabled close>Send</button>
|
||||||
<button class="button" title="ESCAPE" close>Cancel</button>
|
<button class="button" type="button" title="ESCAPE" close>Cancel</button>
|
||||||
</div>
|
</div>
|
||||||
</x-paper>
|
</x-paper>
|
||||||
</x-background>
|
</x-background>
|
||||||
|
|||||||
@@ -524,6 +524,7 @@ class RTCPeer extends Peer {
|
|||||||
this._peerId = peerId;
|
this._peerId = peerId;
|
||||||
this._conn = new RTCPeerConnection(window.rtcConfig);
|
this._conn = new RTCPeerConnection(window.rtcConfig);
|
||||||
this._conn.onicecandidate = e => this._onIceCandidate(e);
|
this._conn.onicecandidate = e => this._onIceCandidate(e);
|
||||||
|
this._conn.onicecandidateerror = e => this._onError(e);
|
||||||
this._conn.onconnectionstatechange = _ => this._onConnectionStateChange();
|
this._conn.onconnectionstatechange = _ => this._onConnectionStateChange();
|
||||||
this._conn.oniceconnectionstatechange = e => this._onIceConnectionStateChange(e);
|
this._conn.oniceconnectionstatechange = e => this._onIceConnectionStateChange(e);
|
||||||
}
|
}
|
||||||
|
|||||||
+25
-7
@@ -632,6 +632,7 @@ class ReceiveFileDialog extends ReceiveDialog {
|
|||||||
|
|
||||||
createPreviewElement(file) {
|
createPreviewElement(file) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
|
try {
|
||||||
let mime = file.type.split('/')[0]
|
let mime = file.type.split('/')[0]
|
||||||
let previewElement = {
|
let previewElement = {
|
||||||
image: 'img',
|
image: 'img',
|
||||||
@@ -642,16 +643,23 @@ class ReceiveFileDialog extends ReceiveDialog {
|
|||||||
if (Object.keys(previewElement).indexOf(mime) === -1) {
|
if (Object.keys(previewElement).indexOf(mime) === -1) {
|
||||||
resolve(false);
|
resolve(false);
|
||||||
} else {
|
} else {
|
||||||
console.log('the file is able to preview');
|
|
||||||
let element = document.createElement(previewElement[mime]);
|
let element = document.createElement(previewElement[mime]);
|
||||||
element.src = URL.createObjectURL(file);
|
|
||||||
element.controls = true;
|
element.controls = true;
|
||||||
element.onload = _ => {
|
element.onload = _ => {
|
||||||
this.$previewBox.appendChild(element);
|
this.$previewBox.appendChild(element);
|
||||||
resolve(true)
|
resolve(true);
|
||||||
};
|
};
|
||||||
element.addEventListener('loadeddata', _ => resolve(true));
|
element.onloadeddata = _ => {
|
||||||
element.onerror = _ => reject(`${mime} preview could not be loaded from type ${file.type}`);
|
this.$previewBox.appendChild(element);
|
||||||
|
resolve(true);
|
||||||
|
};
|
||||||
|
element.onerror = _ => {
|
||||||
|
reject(`${mime} preview could not be loaded from type ${file.type}`);
|
||||||
|
};
|
||||||
|
element.src = URL.createObjectURL(file);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
reject(`preview could not be loaded from type ${file.type}`);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -734,7 +742,6 @@ class ReceiveFileDialog extends ReceiveDialog {
|
|||||||
setTimeout(_ => this.$downloadBtn.style.pointerEvents = "unset", 2000);
|
setTimeout(_ => this.$downloadBtn.style.pointerEvents = "unset", 2000);
|
||||||
};
|
};
|
||||||
|
|
||||||
this.createPreviewElement(files[0]).finally(_ => {
|
|
||||||
document.title = files.length === 1
|
document.title = files.length === 1
|
||||||
? 'File received - PairDrop'
|
? 'File received - PairDrop'
|
||||||
: `${files.length} Files received - PairDrop`;
|
: `${files.length} Files received - PairDrop`;
|
||||||
@@ -742,12 +749,23 @@ class ReceiveFileDialog extends ReceiveDialog {
|
|||||||
Events.fire('set-progress', {peerId: peerId, progress: 1, status: 'process'})
|
Events.fire('set-progress', {peerId: peerId, progress: 1, status: 'process'})
|
||||||
this.show();
|
this.show();
|
||||||
|
|
||||||
|
setTimeout(_ => {
|
||||||
if (canShare) {
|
if (canShare) {
|
||||||
this.$shareBtn.click();
|
this.$shareBtn.click();
|
||||||
} else {
|
} else {
|
||||||
this.$downloadBtn.click();
|
this.$downloadBtn.click();
|
||||||
}
|
}
|
||||||
}).catch(r => console.error(r));
|
}, 500);
|
||||||
|
|
||||||
|
this.createPreviewElement(files[0])
|
||||||
|
.then(canPreview => {
|
||||||
|
if (canPreview) {
|
||||||
|
console.log('the file is able to preview');
|
||||||
|
} else {
|
||||||
|
console.log('the file is not able to preview');
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(r => console.error(r));
|
||||||
}
|
}
|
||||||
|
|
||||||
_downloadFilesIndividually(files) {
|
_downloadFilesIndividually(files) {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
const cacheVersion = 'v1.4.3';
|
const cacheVersion = 'v1.4.5';
|
||||||
const cacheTitle = `pairdrop-cache-${cacheVersion}`;
|
const cacheTitle = `pairdrop-cache-${cacheVersion}`;
|
||||||
const urlsToCache = [
|
const urlsToCache = [
|
||||||
'index.html',
|
'index.html',
|
||||||
|
|||||||
+8
-3
@@ -22,13 +22,18 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
min-height: 100vh;
|
height: 100%;
|
||||||
/* mobile viewport bug fix */
|
/* mobile viewport bug fix */
|
||||||
min-height: -webkit-fill-available;
|
min-height: -moz-available; /* WebKit-based browsers will ignore this. */
|
||||||
|
min-height: -webkit-fill-available; /* Mozilla-based browsers will ignore this. */
|
||||||
|
min-height: fill-available;
|
||||||
}
|
}
|
||||||
|
|
||||||
html {
|
html {
|
||||||
height: -webkit-fill-available;
|
height: 100%;
|
||||||
|
min-height: -moz-available; /* WebKit-based browsers will ignore this. */
|
||||||
|
min-height: -webkit-fill-available; /* Mozilla-based browsers will ignore this. */
|
||||||
|
min-height: fill-available;
|
||||||
}
|
}
|
||||||
|
|
||||||
.row-reverse {
|
.row-reverse {
|
||||||
|
|||||||
@@ -125,7 +125,7 @@
|
|||||||
<div class="font-subheading center text-center">Enter key from another device to continue.</div>
|
<div class="font-subheading center text-center">Enter key from another device to continue.</div>
|
||||||
<div class="center row-reverse">
|
<div class="center row-reverse">
|
||||||
<button class="button" type="submit" disabled>Pair</button>
|
<button class="button" type="submit" disabled>Pair</button>
|
||||||
<button class="button" close>Cancel</button>
|
<button class="button" type="button" close>Cancel</button>
|
||||||
</div>
|
</div>
|
||||||
</x-paper>
|
</x-paper>
|
||||||
</x-background>
|
</x-background>
|
||||||
@@ -140,7 +140,7 @@
|
|||||||
<div class="font-subheading center text-center">Are you sure to unpair all devices?</div>
|
<div class="font-subheading center text-center">Are you sure to unpair all devices?</div>
|
||||||
<div class="center row-reverse">
|
<div class="center row-reverse">
|
||||||
<button class="button" type="submit">Unpair Devices</button>
|
<button class="button" type="submit">Unpair Devices</button>
|
||||||
<button class="button" close>Cancel</button>
|
<button class="button" type="button" close>Cancel</button>
|
||||||
</div>
|
</div>
|
||||||
</x-paper>
|
</x-paper>
|
||||||
</x-background>
|
</x-background>
|
||||||
@@ -212,7 +212,7 @@
|
|||||||
<div id="text-input" class="textarea" role="textbox" autocapitalize="none" spellcheck="false" autofocus contenteditable></div>
|
<div id="text-input" class="textarea" role="textbox" autocapitalize="none" spellcheck="false" autofocus contenteditable></div>
|
||||||
<div class="center row-reverse">
|
<div class="center row-reverse">
|
||||||
<button class="button" type="submit" title="STR + ENTER" disabled close>Send</button>
|
<button class="button" type="submit" title="STR + ENTER" disabled close>Send</button>
|
||||||
<button class="button" title="ESCAPE" close>Cancel</button>
|
<button class="button" type="button" title="ESCAPE" close>Cancel</button>
|
||||||
</div>
|
</div>
|
||||||
</x-paper>
|
</x-paper>
|
||||||
</x-background>
|
</x-background>
|
||||||
|
|||||||
@@ -535,6 +535,7 @@ class RTCPeer extends Peer {
|
|||||||
this._peerId = peerId;
|
this._peerId = peerId;
|
||||||
this._conn = new RTCPeerConnection(window.rtcConfig);
|
this._conn = new RTCPeerConnection(window.rtcConfig);
|
||||||
this._conn.onicecandidate = e => this._onIceCandidate(e);
|
this._conn.onicecandidate = e => this._onIceCandidate(e);
|
||||||
|
this._conn.onicecandidateerror = e => this._onError(e);
|
||||||
this._conn.onconnectionstatechange = _ => this._onConnectionStateChange();
|
this._conn.onconnectionstatechange = _ => this._onConnectionStateChange();
|
||||||
this._conn.oniceconnectionstatechange = e => this._onIceConnectionStateChange(e);
|
this._conn.oniceconnectionstatechange = e => this._onIceConnectionStateChange(e);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -633,6 +633,7 @@ class ReceiveFileDialog extends ReceiveDialog {
|
|||||||
|
|
||||||
createPreviewElement(file) {
|
createPreviewElement(file) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
|
try {
|
||||||
let mime = file.type.split('/')[0]
|
let mime = file.type.split('/')[0]
|
||||||
let previewElement = {
|
let previewElement = {
|
||||||
image: 'img',
|
image: 'img',
|
||||||
@@ -643,16 +644,23 @@ class ReceiveFileDialog extends ReceiveDialog {
|
|||||||
if (Object.keys(previewElement).indexOf(mime) === -1) {
|
if (Object.keys(previewElement).indexOf(mime) === -1) {
|
||||||
resolve(false);
|
resolve(false);
|
||||||
} else {
|
} else {
|
||||||
console.log('the file is able to preview');
|
|
||||||
let element = document.createElement(previewElement[mime]);
|
let element = document.createElement(previewElement[mime]);
|
||||||
element.src = URL.createObjectURL(file);
|
|
||||||
element.controls = true;
|
element.controls = true;
|
||||||
element.onload = _ => {
|
element.onload = _ => {
|
||||||
this.$previewBox.appendChild(element);
|
this.$previewBox.appendChild(element);
|
||||||
resolve(true)
|
resolve(true);
|
||||||
};
|
};
|
||||||
element.addEventListener('loadeddata', _ => resolve(true));
|
element.onloadeddata = _ => {
|
||||||
element.onerror = _ => reject(`${mime} preview could not be loaded from type ${file.type}`);
|
this.$previewBox.appendChild(element);
|
||||||
|
resolve(true);
|
||||||
|
};
|
||||||
|
element.onerror = _ => {
|
||||||
|
reject(`${mime} preview could not be loaded from type ${file.type}`);
|
||||||
|
};
|
||||||
|
element.src = URL.createObjectURL(file);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
reject(`preview could not be loaded from type ${file.type}`);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -735,7 +743,6 @@ class ReceiveFileDialog extends ReceiveDialog {
|
|||||||
setTimeout(_ => this.$downloadBtn.style.pointerEvents = "unset", 2000);
|
setTimeout(_ => this.$downloadBtn.style.pointerEvents = "unset", 2000);
|
||||||
};
|
};
|
||||||
|
|
||||||
this.createPreviewElement(files[0]).finally(_ => {
|
|
||||||
document.title = files.length === 1
|
document.title = files.length === 1
|
||||||
? 'File received - PairDrop'
|
? 'File received - PairDrop'
|
||||||
: `${files.length} Files received - PairDrop`;
|
: `${files.length} Files received - PairDrop`;
|
||||||
@@ -743,12 +750,23 @@ class ReceiveFileDialog extends ReceiveDialog {
|
|||||||
Events.fire('set-progress', {peerId: peerId, progress: 1, status: 'process'})
|
Events.fire('set-progress', {peerId: peerId, progress: 1, status: 'process'})
|
||||||
this.show();
|
this.show();
|
||||||
|
|
||||||
|
setTimeout(_ => {
|
||||||
if (canShare) {
|
if (canShare) {
|
||||||
this.$shareBtn.click();
|
this.$shareBtn.click();
|
||||||
} else {
|
} else {
|
||||||
this.$downloadBtn.click();
|
this.$downloadBtn.click();
|
||||||
}
|
}
|
||||||
}).catch(r => console.error(r));
|
}, 500);
|
||||||
|
|
||||||
|
this.createPreviewElement(files[0])
|
||||||
|
.then(canPreview => {
|
||||||
|
if (canPreview) {
|
||||||
|
console.log('the file is able to preview');
|
||||||
|
} else {
|
||||||
|
console.log('the file is not able to preview');
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(r => console.error(r));
|
||||||
}
|
}
|
||||||
|
|
||||||
_downloadFilesIndividually(files) {
|
_downloadFilesIndividually(files) {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
const cacheVersion = 'v1.4.3';
|
const cacheVersion = 'v1.4.5';
|
||||||
const cacheTitle = `pairdrop-included-ws-fallback-cache-${cacheVersion}`;
|
const cacheTitle = `pairdrop-included-ws-fallback-cache-${cacheVersion}`;
|
||||||
const urlsToCache = [
|
const urlsToCache = [
|
||||||
'index.html',
|
'index.html',
|
||||||
|
|||||||
@@ -23,13 +23,18 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
min-height: 100vh;
|
height: 100%;
|
||||||
/* mobile viewport bug fix */
|
/* mobile viewport bug fix */
|
||||||
min-height: -webkit-fill-available;
|
min-height: -moz-available; /* WebKit-based browsers will ignore this. */
|
||||||
|
min-height: -webkit-fill-available; /* Mozilla-based browsers will ignore this. */
|
||||||
|
min-height: fill-available;
|
||||||
}
|
}
|
||||||
|
|
||||||
html {
|
html {
|
||||||
height: -webkit-fill-available;
|
height: 100%;
|
||||||
|
min-height: -moz-available; /* WebKit-based browsers will ignore this. */
|
||||||
|
min-height: -webkit-fill-available; /* Mozilla-based browsers will ignore this. */
|
||||||
|
min-height: fill-available;
|
||||||
}
|
}
|
||||||
|
|
||||||
.row-reverse {
|
.row-reverse {
|
||||||
|
|||||||
Reference in New Issue
Block a user