FP
FilePartage — Local file sharing
Python 3 Script • macOS • BSD • Linux • Windows

A single-file tool for sending easilly files over your local network.

This page presents source code as a product: efficient, clean and maintainable.
This code is immediately readable and copyable. This software is designed for quick terminal use to share files with any free LocalSend application via LocalSend Protocol.

This software is designed for advanced users; more accessible tools will follow.

Efficient software
Open-Source software
Compatible with Python 3
Compatible with LocalSend protocol
via REST API
Easy copy and paste
Readable terminal output in an IDE-style layout.
FilePartage.py
#!/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.