├── .gitignore ├── LICENSE ├── README.md ├── bin └── playonkodi.js └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Ritiek Malhotra 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # play-on-kodi 2 | 3 | Stream your local/network content directly on Kodi without having to 4 | setup FTP, SMB or anything else. 5 | 6 | ## Installation 7 | 8 | Install the latest release with: 9 | ``` 10 | $ npm install -g playonkodi 11 | ``` 12 | 13 | If you plan to hack on the code, you can instead install the development version with: 14 | ``` 15 | $ git clone https://github.com/ritiek/play-on-kodi 16 | $ cd play-on-kodi 17 | $ npm install -g . 18 | ``` 19 | 20 | Also make sure you have 21 | [youtube-dl](https://github.com/rg3/youtube-dl/blob/master/README.md#installation) installed. 22 | 23 | ## Usage 24 | 25 | ``` 26 | usage: playonkodi.js [-h] [-v] -s SERVER -p PORT [-u USER] [-P PASS] [-i INTERFACE_IP] MEDIA 27 | 28 | Stream your local/network content directly on Kodi. 29 | 30 | Positional arguments: 31 | MEDIA Path to media file 32 | 33 | Optional arguments: 34 | -h, --help Show this help message and exit. 35 | -v, --version Show program's version number and exit. 36 | -s SERVER, --server SERVER 37 | Kodi's local ip address 38 | -p PORT, --port PORT Kodi's web interface port 39 | -u USER, --username USER 40 | [Optional] Kodi's web interface username 41 | -P PASS, --password PASS 42 | [Optional] Kodi's web interface password 43 | -i INTERFACE_IP, --interface-ip INTERFACE_IP 44 | [Optional] Interface IP to send to Kodi server 45 | ``` 46 | 47 | ### Examples 48 | 49 | Stream a local video to Kodi 50 | ``` 51 | $ playonkodi -s 192.168.0.108 -p 6050 /path/to/local/media/file 52 | ``` 53 | 54 | Stream a video from the internet to Kodi 55 | ``` 56 | $ playonkodi -s 192.168.0.108 -p 6050 http://path/to/media 57 | ``` 58 | 59 | Since this tool now uses the youtube-dl backend to resolve URLs, so you should 60 | be able to media content from most websites (including YouTube, HotStar, and many more). 61 | Just pass the URL, and you'll know if it plays on Kodi. 62 | 63 | Send local IP address to Kodi server manually (useful if script cannot find out 64 | the correct network interface IP automatically) 65 | ``` 66 | $ playonkodi -s 192.168.0.108 -p 6050 -i 192.168.0.105 /path/to/local/media/file 67 | ``` 68 | 69 | ## Loading External Subtitles 70 | 71 | There maybe cases where you would want to link your local media content with external 72 | subtitles (.srt, etc.). Due to a limitation in Kodi, it cannot be done automatically 73 | (check out [issue #3](https://github.com/ritiek/play-on-kodi/issues/3)). 74 | 75 | However you can install `mkvtoolnix` (`$ sudo apt install mkvtoolnix`) to embed 76 | external subtitles in the container itself and pass this new container to Kodi. 77 | 78 | **For example:** 79 | ``` 80 | $ mkvmerge -o output.mkv input.mp4 subtitles.srt 81 | $ playonkodi -s 192.168.0.108 -p 6050 output.mkv 82 | ``` 83 | 84 | ## Why? 85 | 86 | This tool is supposed is supposed to be very minimal way to play local and 87 | network files on Kodi. It was made to quickly be able to play your local media 88 | content to Kodi server. You don't want to setup FTP/SMB (if not already), add it 89 | as a network source on Kodi and locate the media to just make the thing play 90 | 91 | 92 | ## How it works? 93 | 94 | - For local media, it makes your media content available locally to the devices 95 | on the same network. Otherwise it just uses youtube-dl to resolve the passed URL. 96 | 97 | - It then attempts to figure out your PC's local IP address. 98 | 99 | - And lastly, it makes a network request to Kodi's jsonrpc server to play the 100 | hosted media content. 101 | 102 | 103 | 104 | # License 105 | 106 | `[![License](https://img.shields.io/github/license/ritiek/play-on-kodi.svg)](https://github.com/ritiek/play-on-kodi/blob/master/LICENSE) 107 | -------------------------------------------------------------------------------- /bin/playonkodi.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var ArgumentParser = require('argparse').ArgumentParser; 4 | var http = require('http'); 5 | var request = require('request'); 6 | 7 | var finalhandler = require('finalhandler'); 8 | var serveStatic = require('serve-static'); 9 | var path = require('path'); 10 | var os = require('os'); 11 | var exec = require('child_process').exec; 12 | 13 | 14 | function parse_args() { 15 | var parser = new ArgumentParser({ 16 | version: '0.3.1', 17 | addHelp: true, 18 | description: 'Stream your local/network content directly on Kodi.', 19 | }); 20 | parser.addArgument( 21 | [ 'media' ], { 22 | metavar: 'MEDIA', 23 | type: 'string', 24 | help: 'Path to media file' 25 | } 26 | ); 27 | parser.addArgument( 28 | [ '-s', '--server' ], { 29 | type: 'string', 30 | required: true, 31 | help: "Kodi's local ip address" 32 | } 33 | ); 34 | parser.addArgument( 35 | [ '-p', '--port' ], { 36 | type: 'int', 37 | required: true, 38 | help: "Kodi's web interface port" 39 | } 40 | ); 41 | parser.addArgument( 42 | [ '-u', '--username' ], { 43 | type: 'string', 44 | required: false, 45 | help: "Kodi's web interface username" 46 | } 47 | ); 48 | parser.addArgument( 49 | [ '-P', '--password' ], { 50 | type: 'string', 51 | required: false, 52 | help: "Kodi's web interface password" 53 | } 54 | ); 55 | parser.addArgument( 56 | [ '-i', '--interface-ip' ], { 57 | type: 'string', 58 | help: "[Optional] Interface IP to send to Kodi server" 59 | } 60 | ); 61 | parser.addArgument( 62 | [ '--no-resolve' ], { 63 | action: 'storeTrue', 64 | help: "[Optional] Do not resolve the external media URL with youtube-dl" 65 | } 66 | ); 67 | return parser; 68 | } 69 | 70 | 71 | function get_localip() { 72 | var ifaces = os.networkInterfaces(); 73 | var localip = ''; 74 | 75 | Object.keys(ifaces).forEach(function (ifname) { 76 | ifaces[ifname].forEach(function (iface) { 77 | if ('IPv4' !== iface.family || iface.internal !== false) { 78 | // skip over internal (i.e. 127.0.0.1) and non-ipv4 addresses 79 | return; 80 | } 81 | localip = iface.address; 82 | }); 83 | }); 84 | 85 | return localip; 86 | } 87 | 88 | 89 | function serve_directory(directory, port) { 90 | var serve = serveStatic(directory); 91 | 92 | var server = http.createServer(function(req, res) { 93 | var done = finalhandler(req, res); 94 | serve(req, res, done); 95 | }); 96 | 97 | server.listen(port); 98 | } 99 | 100 | 101 | function kodi_post(network_file, server, port, username, password) { 102 | console.log('Commanding jsonrpc on ' + server_ip + ':' + server_port + ' to listen for media content on the resolved URL'); 103 | var dataString = '{"id":519,"jsonrpc":"2.0","method":"Player.Open","params":{"item":{"file":"' + network_file + '"}}}'; 104 | 105 | var options = { 106 | url: 'http://' + server + ':' + port + '/jsonrpc', 107 | method: 'POST', 108 | body: dataString 109 | }; 110 | 111 | if (username || password) { 112 | var auth_string = username + ':' + password 113 | options.headers = { 114 | "Authorization": "Basic " + Buffer.from(auth_string, 'utf-8').toString('base64') 115 | } 116 | } 117 | 118 | console.log('The media content should play now'); 119 | return request(options); 120 | } 121 | 122 | function execute(command, callback) { 123 | exec(command, function(error, stdout, stderr) { 124 | callback(stdout); 125 | }); 126 | }; 127 | 128 | var parser = parse_args(); 129 | var args = parser.parseArgs(); 130 | 131 | const filepath = args.media 132 | const server_ip = args.server 133 | const server_port = args.port 134 | const server_username = args.username 135 | const server_password = args.password 136 | 137 | 138 | if (filepath.indexOf('://') == -1) { 139 | var local_ip = args.interface_ip; 140 | if (local_ip == null) { 141 | local_ip = get_localip(); 142 | } 143 | const local_port = '15000'; 144 | console.log(local_ip); 145 | 146 | var directory = path.dirname(filepath); 147 | var network_file = 'http://' + local_ip + ':' + local_port + '/' + encodeURIComponent(path.basename(filepath)); 148 | 149 | console.log('Hosting media content on:') 150 | console.log(network_file + '\n'); 151 | 152 | serve_directory(directory, local_port); 153 | kodi_post(network_file, server_ip, server_port, server_username, server_password); 154 | 155 | console.log('\nHit Ctrl+C to kill the local stream server'); 156 | 157 | } else { 158 | var to_resolve = !args.no_resolve; 159 | if (to_resolve) { 160 | console.log('Resolving URL using youtube-dl'); 161 | execute('youtube-dl -gf best ' + filepath, function(resolved) { 162 | resolved = resolved.replace('\n', ''); 163 | kodi_post(resolved, server_ip, server_port); 164 | }); 165 | } else { 166 | kodi_post(filepath, server_ip, server_port); 167 | } 168 | } 169 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "playonkodi", 3 | "description": "Stream your local/network content directly on Kodi without FTP, SMB or anything else", 4 | "version": "0.3.1", 5 | "keywords": [ 6 | "kodi", 7 | "local", 8 | "media", 9 | "http", 10 | "url", 11 | "stream" 12 | ], 13 | "contributors": [ 14 | "Ritiek Malhotra" 15 | ], 16 | "license": "MIT", 17 | "repository": "ritiek/play-on-kodi", 18 | "dependencies": { 19 | "argparse": "~1.0.9", 20 | "request": "~2.81.0", 21 | "finalhandler": "~1.0.4", 22 | "serve-static": "~1.12.4", 23 | "path": "~0.12.7", 24 | "os": "~0.1.1", 25 | "http": "~0.0.0" 26 | }, 27 | "bin": { 28 | "playonkodi" : "bin/playonkodi.js" 29 | } 30 | } 31 | --------------------------------------------------------------------------------