diff --git a/README.md b/README.md index 89d52be..4c0f615 100644 --- a/README.md +++ b/README.md @@ -52,7 +52,7 @@ pip3 install ./opendrop We briefly explain how to send and receive files using `opendrop`. To see all command line options, run `opendrop -h`. -### Sending a File +### Sending a File or a Link Sending a file is typically a two-step procedure. You first discover devices in proximity using the `find` command. Stop the process once you have found the receiver. @@ -62,7 +62,7 @@ Looking for receivers. Press Ctrl+C to stop ... Found index 0 ID eccb2f2dcfe7 name John’s iPhone Found index 1 ID e63138ac6ba8 name Jane’s MacBook Pro ``` -You can then `send` a file using +You can then `send` a file (or link, see below) using ``` $ opendrop send -r 0 -f /path/to/some/file Asking receiver to accept ... @@ -73,6 +73,13 @@ Uploading has been successful Instead of the `index`, you can also use `ID` or `name`. OpenDrop will try to interpret the input in the order (1) `index`, (2) `ID`, and (3) `name` and fail if no match was found. +**Sending a web link.** Since v0.13, OpenDrop supports sending web links, i.e., URLs, so that receiving Apple devices will immediately open their browser upon accepting. +(Note that OpenDrop _receivers_ still only support receiving regular files.) + +``` +$ opendrop send -r 0 -f https://owlink.org --url +``` + ### Receiving Files Receiving is much easier. Simply use the `receive` command. OpenDrop will accept all incoming files automatically and put received files in the current directory. diff --git a/opendrop/cli.py b/opendrop/cli.py index 85c68e9..88d8f34 100644 --- a/opendrop/cli.py +++ b/opendrop/cli.py @@ -41,6 +41,7 @@ class AirDropCli: parser = argparse.ArgumentParser() parser.add_argument("action", choices=["receive", "find", "send"]) parser.add_argument("-f", "--file", help="File to be sent") + parser.add_argument("-u", "--url", help="'-f,--file is a URL", action="store_true") parser.add_argument( "-r", "--receiver", @@ -99,9 +100,10 @@ class AirDropCli: else: # args.action == 'send' if args.file is None: parser.error("Need -f,--file when using send") - if not os.path.isfile(args.file): + if not os.path.isfile(args.file) and not args.url: parser.error("File in -f,--file not found") self.file = args.file + self.is_url = args.url if args.receiver is None: parser.error("Need -r,--receiver when using send") self.receiver = args.receiver @@ -184,12 +186,12 @@ class AirDropCli: return self.client = AirDropClient(self.config, (info["address"], info["port"])) logger.info("Asking receiver to accept ...") - if not self.client.send_ask(self.file): + if not self.client.send_ask(self.file, is_url=self.is_url): logger.warning("Receiver declined") return logger.info("Receiver accepted") logger.info("Uploading file ...") - if not self.client.send_upload(self.file): + if not self.client.send_upload(self.file, is_url=self.is_url): logger.warning("Uploading has failed") return logger.info("Uploading has been successful") diff --git a/opendrop/client.py b/opendrop/client.py index 471aeff..5ce8d43 100644 --- a/opendrop/client.py +++ b/opendrop/client.py @@ -144,7 +144,7 @@ class AirDropClient: # if name is returned, then receiver is discoverable return response.get("ReceiverComputerName") - def send_ask(self, file_path, icon=None): + def send_ask(self, file_path, is_url=False, icon=None): ask_body = { "SenderComputerName": self.config.computer_name, "BundleID": "com.apple.finder", @@ -155,18 +155,6 @@ class AirDropClient: if self.config.record_data: ask_body["SenderRecordData"] = self.config.record_data - if isinstance(file_path, str): - file_path = [file_path] - - # generate icon for first file - with open(file_path[0], "rb") as f: - file_header = f.read(128) - flp = fleep.get(file_header) - if not icon and len(flp.mime) > 0 and "image" in flp.mime[0]: - icon = AirDropUtil.generate_file_icon(f.name) - if icon: - ask_body["FileIcon"] = icon - def file_entries(files): for file in files: file_name = os.path.basename(file) @@ -179,8 +167,20 @@ class AirDropClient: } yield file_entry - ask_body["Files"] = [e for e in file_entries(file_path)] - ask_body["Items"] = [] + if isinstance(file_path, str): + file_path = [file_path] + if is_url: + ask_body["Items"] = file_path + else: + # generate icon for first file + with open(file_path[0], "rb") as f: + file_header = f.read(128) + flp = fleep.get(file_header) + if not icon and len(flp.mime) > 0 and "image" in flp.mime[0]: + icon = AirDropUtil.generate_file_icon(f.name) + ask_body["Files"] = [e for e in file_entries(file_path)] + if icon: + ask_body["FileIcon"] = icon ask_binary = plistlib.dumps( ask_body, fmt=plistlib.FMT_BINARY # pylint: disable=no-member @@ -189,10 +189,14 @@ class AirDropClient: return success - def send_upload(self, file_path): + def send_upload(self, file_path, is_url=False): """ Send a file to a receiver. """ + # Don't send an upload request if we just sent a link + if is_url: + return + headers = { "Content-Type": "application/x-cpio", }