{"id":18446373,"url":"https://github.com/misterhat/livelook","last_synced_at":"2025-07-17T21:37:21.631Z","repository":{"id":46895790,"uuid":"183915696","full_name":"misterhat/livelook","owner":"misterhat","description":"soulseek client in javascript","archived":false,"fork":false,"pushed_at":"2019-05-08T19:21:04.000Z","size":229,"stargazers_count":96,"open_issues_count":8,"forks_count":10,"subscribers_count":9,"default_branch":"master","last_synced_at":"2025-04-08T00:34:17.103Z","etag":null,"topics":["javascript","music","nodejs","p2p","soulseek"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"agpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/misterhat.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"COPYING","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-04-28T14:02:47.000Z","updated_at":"2025-02-14T04:36:07.000Z","dependencies_parsed_at":"2022-08-28T11:23:01.847Z","dependency_job_id":null,"html_url":"https://github.com/misterhat/livelook","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/misterhat/livelook","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/misterhat%2Flivelook","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/misterhat%2Flivelook/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/misterhat%2Flivelook/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/misterhat%2Flivelook/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/misterhat","download_url":"https://codeload.github.com/misterhat/livelook/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/misterhat%2Flivelook/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":265663122,"owners_count":23807465,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["javascript","music","nodejs","p2p","soulseek"],"created_at":"2024-11-06T07:09:08.371Z","updated_at":"2025-07-17T21:37:21.590Z","avatar_url":"https://github.com/misterhat.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# livelook 👀\n\u003cimg align=\"right\" width=\"176\" height=\"220\" src=\"./livelook.svg\"\u003e\n\na [soulseek](https://en.wikipedia.org/wiki/Soulseek) client written in\njavascript. soulseek allows users from around the world to connect to each other\ndirectly and share music (and other stuff).\n\n\u003eSoulseek is an ad-free, spyware free, just plain free file sharing network for\n\u003eWindows, Mac and Linux. Our rooms, search engine and search correlation system\n\u003emake it easy for you to find people with similar interests, and make new\n\u003ediscoveries!\n\u003e\n\u003e-- [About Soulseek | Soulseek](https://www.slsknet.org/news/node/680)\n\nfeatures supported:\n* [nat pmp](https://en.wikipedia.org/wiki/NAT_Port_Mapping_Protocol) and\n[upnp](https://en.wikipedia.org/wiki/Universal_Plug_and_Play) port-forwarding\n* chat rooms and private messages (including direct connections)\n* browsing user's share lists and vice versa\n* searching the network's files and responding to distributed searches\n* downloading and uploading with automatically updating share list\n\n\u003cdiv style=\"clear: both;\"\u003e\ni mainly tested this against nicotine-plus, but it works with soulseekqt too.\n\u003c/div\u003e\n\n## example\n```javascript\nconst LiveLook = require('./');\nconst fs = require('fs');\n\nlet livelook = new LiveLook({\n    username: 'toadtripler',\n    password: 'not my password',\n    sharedFolder: './mp3s',\n    autojoin: [ 'nicotine' ]\n});\n\nlivelook.on('error', console.error);\n\nlivelook.login((err, res) =\u003e {\n    if (err || !res.success) {\n        return console.log('login failed');\n    }\n\n    livelook.on('sayChatroom', msg =\u003e {\n        console.log(`[${msg.room}] \u003c${msg.username}\u003e ${msg.message}`);\n    });\n\n    livelook.on('messageUser', msg =\u003e {\n        console.log(`\u003c${msg.username}\u003e ${msg.message}`);\n    });\n\n    livelook.search('hot dad web love', (err, res) =\u003e {\n        if (err) {\n            return console.error(err);\n        }\n\n        res = res.filter(item =\u003e item.slotsFree);\n\n        if (!res) {\n            console.log('no files found :(');\n            return;\n        }\n\n        let downloaded = fs.createWriteStream('hot-dad-web-love.mp3');\n        res = res.sort((a, b) =\u003e a.speed \u003e b.speed ? -1: 0)[0];\n        livelook.downloadFile(res.username, res.file).pipe(downloaded);\n        downloaded.on('end', () =\u003e {\n            livelook.messageUser(res.username, `hey thanks for ${res.file}!`);\n        });\n    });\n});\n```\n## install\n\n\t$ npm install --save livelook\n\n## api\n### livelook = new LiveLook(args)\ncreate a new `livelook` instance.\n\n```javascript\n// args\n{\n    username: '',\n    password: '',\n    server: 'server.slsknet.org',\n    port: 2242, // port for server above, NOT the port we listen on\n    waitPort: 2234, // port for peer server. will retry multiple options if fail\n    sharedFolder: './mp3s',\n    downloadFolder: './downloads',\n    description: 'user biography',\n    autojoin: [ 'chatrooms', 'joined', 'automatically' ],\n    maxPeers: 100,\n    uploadSlots: 2, // maximum uploads allowed at one time\n    uploadThrottle: 56 * 1024, // speed to throttle uploads in bytes\n    downloadThrottle: 56 * 1024\n}\n```\n\n### livelook.init([done])\ninitialize our share list and connect to the soulseek server. you don't need to\ncall this if you just use login below.\n\n### livelook.login([username, password, done])\nlogin to the soulseek server, and initialize our peer server if it isn't\nalready. username and password are optional if the instance has them.\n\n### livelook.refreshShareList([done])\nre-scan `livelook.sharedFolder` and repopulate `livelook.shareList`. this is\nwhat other users see when they browse us, or when we respond to searches.\n\n### livelook.sayChatroom(room, message)\nsend a message to a chatroom.\n\n### livelook.leaveChatroom(room)\nleave a chatroom and stop receiving messages from it.\n\n### livelook.joinRoom(room)\njoin a chatroom and start accepting messages from it.\n\n### livelook.messageUser(username, message)\nsend a private message to a specific user.\n\n### livelook.setStatus(status)\nset our online/away status.\n\n`status` can be a `Number` (1 for away, 2 for online), `'away'` or `'online'`.\n\n### livelook.refreshUploadSpeed([done])\nre-calculate our upload speed from [speedtest.net](https://www.speedtest.net/).\n\n### livelook.getPeerAddress(username, done)\nget a peer's ip address and port based on their username.\n\n### livelook.getPeerByUsername(username, done)\nget a peer instance based on a username. this will first check our pre-existing\npeers, then it tries to make a direct connection to the peer until finally\nrequesting the server connect the peer to us.\n\n### livelook.getShareFileList(username, done)\nget all the files a user is sharing. may take a while as some people share\nlarge amounts of files, and the message must be decompressed.\n\n### livelook.searchUserShares(username, query, done)\nsearch a user's shares for a query. nicotine users max out at 50 by default.\n\n### livelook.getUserInfo(username, done)\nget a user's description (biography), picture (avatar) as a buffer,\nupload slots, queue size and slots free.\n\n### livelook.getFolderContents(username, folder, done)\nget a list of all the files in the specified folder.\n\n### livelook.downloadFile(username, file, [fileStart = 0])\ndownload a file from a user. this returns a `ReadableStream`, and will also\nemit a `queue` event with its position if we can't download it immediately. pass\nin `fileStart` to indicate where to begin downloading the file in bytes (to\nresume interrupted downloads).\n\n### livelook.searchFiles(query, [args = { timeout, max }, done])\nsearch other peers for files. this returns a `ReadableStream` in `objectMode`.\non search results it will emit `data` events containing the following:\n\n```javascript\n{ username, file, size, bitrate, vbr, duration, slotsFree, speed, queueSize }\n```\n\nif `done` is provided, the terms will be concatted and passed in either after\nthe timeout finishes or the max results are reached.\n\n## how it works\nsoulseek is largely peer-to-peer, but still relies on a central server for\nchat rooms, messaging, piercing firewalls and finding peers.\n\n### initializing\nfirst we set up a local server to accept peer connections (and port forward with\nnat pmp or upnp if possible). then we connect to slsknet.org (or any other\nsoulseek server) as a separate client and login. we can now begin to chat and\nbrowse.\n\n### finding peers\nwe can connect to peers by fetching their ip and port based on their username\nfrom the soulseek server, but if they aren't port-forwarded this will fail. the\nnext step is to send a connection request via the soulseek server to tell them\nto connect to us. if this fails, there is no way for them to connect to us. this\nis why it's a good idea to enable nat pmp or port forward manually.\n\n### searching\nafter logging in, we tell the server we're orphaned and have no parents. after\nan arbitrary amount of time (usually around 30 seconds), the server gives a list\nof potential parents. once we connect to one successfully, we can also become a\nparent. our parent will contionously send us search requests from other peers.\nwe can respond to them directly if we have the results, but also send the\nrequest to all of our children so they can do the same.\n\n## see also\n* [Soulseek.NET](https://github.com/jpdillingham/Soulseek.NET) by @jpdillingham\n* [museek-plus](https://github.com/eLvErDe/museek-plus) by @eLvErDe\n    * [protocol docs](https://htmlpreview.github.io/?http://github.com/misterhat/livelook/blob/master/doc/SoulseekProtocol%20%E2%80%93%20Museek%2B.html)\n* [nicotine-plus](https://github.com/Nicotine-Plus/nicotine-plus)\n* [slsk-client](https://github.com/f-hj/slsk-client) by @f-hj.\n* [soleseek protocol docs](https://htmlpreview.github.io/?https://github.com/misterhat/livelook/blob/master/doc/soulseek_protocol.html)\n\n## donate\n[donate to keep the central server alive!](https://www.slsknet.org/donate.php)\n\n## license\nCopyright (C) 2019  Zorian Medwid\n\nThis program is free software: you can redistribute it and/or modify\nit under the terms of the GNU Affero General Public License as\npublished by the Free Software Foundation, either version 3 of the\nLicense, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU Affero General Public License for more details.\n\nYou should have received a copy of the GNU Affero General Public License\nalong with this program.  If not, see http://www.gnu.org/licenses/.\n\n**You may not distribute this program without offering the source code. Hosting\na web service that utilizes livelook is distrubtion.**\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmisterhat%2Flivelook","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmisterhat%2Flivelook","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmisterhat%2Flivelook/lists"}