#!/usr/bin/env python3
"""
FilePartage
Send any file to a phone/laptop/computer via the LocalSend protocol.
Author: Julien Perrissin-Fabert
Version: 20190217
Prerequisites:
pip install requests
How to install requests:
python -m ensurepip --default-pip
python3 -m pip install requests
Usage:
$ python3 FilePartage.py [Recipient's IP address] /path/to/my_archive.zip
Example:
$ python3 FilePartage.py 192.168.1.42 my_archive.zip
"""
import argparse
import os
import uuid
import requests
import urllib3
# LocalSend uses a self-signed certificate -> we disable the warning.
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
def send_file(
server_host: str,
file_path: str,
port: int = 53317,
alias: str = "FilePartage App",
device_model: str = "macOS",
device_type: str = "desktop",
use_https: bool = True,
) -> None:
"""
Sends a single file to the remote LocalSend server (v1 protocol).
"""
if not os.path.isfile(file_path):
raise FileNotFoundError(f"File not found: {file_path}")
scheme = "https" if use_https else "http"
base_url = f"{scheme}://{server_host}:{port}/api/localsend/v1"
# File metadata (see v1.md: ‘files’ field indexed by fileId)
file_id = str(uuid.uuid4())
file_size = os.path.getsize(file_path)
file_name = os.path.basename(file_path)
# For a .zip file, you can set fileType="other"
send_request_payload = {
"info": {
"alias": alias,
"deviceModel": device_model,
"deviceType": device_type, # "mobile" | "desktop" | "web"
},
"files": {
file_id: {
"id": file_id,
"fileName": file_name,
"size": file_size,
"fileType": "other",
"preview": None, # no thumbnail
}
},
}
# 1) Sending the metadata request
print(f"[1/2] Sending the metadata request to {base_url}/send-request ...")
resp = requests.post(
f"{base_url}/send-request",
json=send_request_payload,
timeout=10,
verify=False, # self-signed certificate
)
resp.raise_for_status()
tokens = resp.json()
if file_id not in tokens:
raise RuntimeError(
f"The recipient likely rejected the file (id {file_id} not included in the response): {tokens}"
)
token = tokens[file_id]
print(f"Token received for the file {file_name}: {token}")
# 2) Sending binary content
send_url = f"{base_url}/send"
params = {"fileId": file_id, "token": token}
print(f"[2/2] Sending binary data to {send_url} ...")
with open(file_path, "rb") as f:
resp = requests.post(
send_url,
params=params,
data=f,
timeout=None, # allow enough time
verify=False,
headers={"Content-Type": "application/octet-stream"},
)
resp.raise_for_status()
print("Transfer completed successfully.")
def main():
parser = argparse.ArgumentParser(
description="Send any file to a phone/laptop/computer via the LocalSend protocol."
)
parser.add_argument(
"host",
help="The phone's IP address or hostname (on the same local network, with LocalSend open).",
)
parser.add_argument(
"file",
help="Path to the .zip file to be sent.",
)
parser.add_argument(
"--port",
type=int,
default=53317,
help="The phone's LocalSend port (default: 53317).",
)
parser.add_argument(
"--alias",
default="MacBook",
help="The name or alias of the transmitting device displayed on the phone.",
)
parser.add_argument(
"--device-model",
default="macOS",
help="Transmitter model (optional information).",
)
parser.add_argument(
"--device-type",
default="desktop",
choices=["mobile", "desktop", "web"],
help="Transmitter device type (default: desktop).",
)
parser.add_argument(
"--no-https",
action="store_true",
help="Use HTTP instead of HTTPS (not recommended).",
)
args = parser.parse_args()
send_file(
server_host=args.host,
file_path=args.file,
port=args.port,
alias=args.alias,
device_model=args.device_model,
device_type=args.device_type,
use_https=not args.no_https,
)
if __name__ == "__main__":
main()
Tip: Click “Copy code” to get the script right away.