hexxeh / spotify-websocket-api Goto Github PK
View Code? Open in Web Editor NEWSpotify Websocket API is a fully open source Spotify library based on the WebSocket API used in the Spotify Web beta
Spotify Websocket API is a fully open source Spotify library based on the WebSocket API used in the Spotify Web beta
Hi there, firstly, thank you for this awesome tool! It's really useful!
On Thursday, because of the authentication fix, I decided to re-install on my Raspberry Pi. Unfortunately I'm getting the errors in this pastebin: http://pastebin.com/yu0w9Qsr. I was wondering if you could help me figure out what it is? Sorry, I know it's not a direct issue with your code.
After the compilation failed I decided to see if the example client would work. It kind of does, but the audio is very quiet and fuzzy. I assume it must have something to do with the compilation failure?
Thanks again!
I'm wondering if Spotify is kinda of blocking access.
Is it working for you?
I've got a premium account, btw.
>>> sp = friendly.Spotify(username, password)
[ERROR] unknown (11) - failed to send to backend
Is 'mp3160' the onliest available quality, or how are other variables called?
I don't know, if I'm just too stupid, or if Facebook login credentials really don't work.
When trying to get track url, "Track error" is returned (search etc. functions work). Track is playable in browser and desktop client.
What is the reason (may be it is connected with depreciation of API v0.x on December 17, see http://devnews.spotify.com/2013/12/17/deprecation-of-spotify-apps-api-0-x/)?
Python 2.7 (32), Windows 8 (64), last version of module from git.
Test script:
from spotify_web.spotify import SpotifyAPI, SpotifyUtil, Logging
import sys
Logging.log_level=3
sp = SpotifyAPI()
sp.connect(sys.argv[1], sys.argv[2])
uri = "spotify:track:0dEIca2nhcxDUV8C5QkPYb"
track = sp.metadata_request(uri)
print track.artist[0].name + " - " + track.name
url = sp.track_uri(track)
print url
Result:
[NOTICE] Connecting to wss://lon2-linkap-a2.ap.spotify.com/
[NOTICE] Logging in
[DEBUG] sent {"args":["201","ba30f56ee228847cf4c5be0b5b489aabbb1e1826","{\"ip\":\"212.93.100.58\",\"timestamp\":1388449746,\"ttl\":900,\"useragent\":\"node-spotify-web in python (Chrome\\/13.37 compatible-ish)\",\"version\":62300110,\"token\":\"NApfCkcKDVNwb3RpZnktdXNlcnMSCGl2YXJzNzc3GgOYAQElVhPCUjIiCgikFh6yf9gj8hIW3y6eKvqboXhSjzYpgMCX9NgnsgJ1txIUVKzwgXZEGWrUTWAkuHxEHk6mJJ4\"}"],"name":"connect","id":"0"}
[DEBUG] recv {"id":0,"result":"ok"}
[DEBUG] sent {"args":[],"name":"sp/user_info","id":"1"}
[DEBUG] recv {"message":["do_work","var t2=5; try{ for(var i=0; i<720094129..toString(32<<0).length;i++) t2 = t2+29627..toString(32<<0).length;}catch(_) {}; this.reply('' + t2);"]}
[DEBUG] Got do_work message, payload: var t2=5; try{ for(var i=0; i<720094129..toString(32<<0).length;i++) t2 = t2+29627..toString(32<<0).length;}catch(_) {}; this.reply('' + t2);
[DEBUG] sent {"args":["v1"],"name":"sp/work_done","id":"2"}
[DEBUG] recv {"message":["login_complete"]}
[DEBUG] recv {"id":1,"result":{"ab_collection_union":"0","ab_test_group":"698","ads":"0","app_developer":"0","catalogue":"premium","country":"LV","head_files":"0","head_files_url":"http://heads.spotify.com/head/{file_id}","lastfm_session":"","license_agreements":"0.8.8-LV","link_tutorial_completed":"1","post_open_graph":"-2","preferred_locale":"en","product":"premium","public_toplist":"3","publish_activity":"0","publish_playlist":"0","user":"ivars777","wanted_licenses":"0.8.8-LV"}}
[DEBUG] sent {"args":"h","name":"sp/echo","id":"3"}
[DEBUG] recv {"id":2,"result":"ok"}
[DEBUG] Got ack for message reply
[DEBUG] sent {"args":[0,"CjRobTovL21ldGFkYXRhL3RyYWNrLzA3M2NjMjE2MjY4ZjQxOWZhMjEzNjE0NGQyMGJiMGRmGgNH\nRVQ=\n"],"name":"sp/hm_b64","id":"4"}
[DEBUG] recv {"id":3,"result":""}
[DEBUG] No callback was requested for command 3, ignoring
[DEBUG] recv {"id":4,"result":["CjRobTovL21ldGFkYXRhL3RyYWNrLzA3M2NjMjE2MjY4ZjQxOWZhMjEzNjE0NGQyMGJiMGRmEhp2bmQuc3BvdGlmeS9tZXRhZGF0YS10cmFjayCQAzIYCgpNRC1WZXJzaW9uEgoxMzg4MzUzMTk4Mg8KBk1DLVRUTBIFNjY1ODcyGQoPTUMtQ2FjaGUtUG9saWN5EgZwdWJsaWMyDwoHTUMtRVRhZxIExF0lQA==","ChAHPMIWJo9Bn6ITYUTSC7DfEhdHaXZlIExpZmUgQmFjayB0byBNdXNpYxqfAgoQjxrCACoPTuSuiYHnX3cs6hIWUmFuZG9tIEFjY2VzcyBNZW1vcmllcxodChCTUmFuG2pKX76HZKFki1GsEglEYWZ0IFB1bmsqCENvbHVtYmlhMgcIuh8QChgiSh4KFFC6YywiG60XMHHeajB/ciPbl4EFEAAY2AQg1ARKHgoUFTf5Oro5W50bz1F9TqqseWbk4eQQARiAASCAAUoeChRsQYbruwuG8JiVTmUYEj/AWo7t3xACGIAKIPgJigFgCh4KFFC6YywiG60XMHHeajB/ciPbl4EFEAAY2AQg1AQKHgoUFTf5Oro5W50bz1F9TqqseWbk4eQQARiAASCAAQoeChRsQYbruwuG8JiVTmUYEj/AWo7t3xACGIAKIPgJIh0KEJNSYW4bakpfvodkoWSLUawSCURhZnQgUHVuaygCMAI45NMhQJQBUhQKBGlzcmMSDFVTUVg5MTMwMDEwMVoyCAAIAQgDGipBU0FXQVhCTEJRQlZDQ0NXQ1hHR0dTSE1JTUpFSlBNRk5GU0pTWFVNVkliGAoUK3PU0rPre5z0Q7IoBu2oHdv2cnAQAmIYChSBRb0UBOC7Z0qwJJA+/0jHaNVvNhABYhgKFE+yAn9k87zujolJ1suqpHohmImuEABiGAoU2evuTZHKZZzTUgCOdUzOjuyz+YEQBGICEAV6GAoUW9YsDJiw8BSQx9VC8ssQ1NM4llgQBg=="]}
Daft Punk - Give Life Back to Music
[NOTICE] spotify:track:dEIca2nhcxDUV8C5QkPYb is available!
[DEBUG] sent {"args":["mp3160","073cc216268f419fa2136144d20bb0df"],"name":"sp/track_uri","id":"5"}
[DEBUG] recv {"error":[12,0,""],"id":5}
[ERROR] Track error
[DEBUG] sent {"args":["mp3160","073cc216268f419fa2136144d20bb0df"],"name":"sp/track_uri","id":"6"}
[DEBUG] recv {"error":[12,0,""],"id":6}
[ERROR] Track error
[DEBUG] sent {"args":"h","name":"sp/echo","id":"7"}
[DEBUG] recv {"id":7,"result":""}
[DEBUG] No callback was requested for command 7, ignoring
[DEBUG] sent {"args":["mp3160","073cc216268f419fa2136144d20bb0df"],"name":"sp/track_uri","id":"8"}
[DEBUG] recv {"error":[12,0,""],"id":8}
[ERROR] Track error
False
When I subscribe to a specific users playlist through the spotify desktop client then call list in respotify I see the following error:
Playlists
[1] Starred
Traceback (most recent call last):
File "respotify.py", line 285, in
command_loop()
File "respotify.py", line 251, in command_loop
command_map[command_name]0
File "respotify.py", line 91, in command_list
print " ["+str(index)+"] "+playlist.getName()
File "../../spotify_web/friendly.py", line 254, in getName
return "Starred" if self.getID() == "starred" else self.obj.attributes.name
AttributeError: 'bool' object has no attribute 'attributes'
Exception in thread Thread-1 (most likely raised during interpreter shutdown):
Traceback (most recent call last):
File "/usr/lib/python2.7/threading.py", line 552, in __bootstrap_inner
File "/usr/lib/python2.7/threading.py", line 505, in run
File "../../spotify_web/spotify.py", line 740, in heartbeat_handler
File "../../spotify_web/spotify.py", line 594, in heartbeat
File "../../spotify_web/spotify.py", line 643, in send_command
<type 'exceptions.TypeError'>: 'NoneType' object is not callable
However if I unsubscribe and then call list it shows me my playlists as expected despite a number of them being playlists I am subscribed to (e.g. Digster playlists) not sure if this is a bug or one of the yet unimplemented social features?
Thanks for any help in advance
I get the following error when I run:
USERNAME=myusername PASSWORD=mypassword python test.py
Output:
[ERROR] There was a problem authenticating, authentication failed
Login failed
E[ERROR] There was a problem authenticating, authentication failed
Login failed
E
======================================================================
ERROR: test_get_track_by_uri (__main__.SpotifyTest)
----------------------------------------------------------------------
Traceback (most recent call last):
File "test.py", line 36, in test_get_track_by_uri
self.assertEqual(reference["title"], track.getName())
AttributeError: 'bool' object has no attribute 'getName'
======================================================================
ERROR: test_playlist_add_delete (__main__.SpotifyTest)
----------------------------------------------------------------------
Traceback (most recent call last):
File "test.py", line 42, in test_playlist_add_delete
before = len(self.spotify.getPlaylists())
File "/media/nexus/Data/Public/test-project/python/spotify-websocket-api/spotify_web/friendly.py", line 42, in __call__
res = cache[key] = self.func(*args, **kw)
File "/media/nexus/Data/Public/test-project/python/spotify-websocket-api/spotify_web/friendly.py", line 449, in getPlaylists
playlist_uris += ["spotify:user:"+username+":starred"]
TypeError: cannot concatenate 'str' and 'NoneType' objects
----------------------------------------------------------------------
Ran 2 tests in 2.921s
FAILED (errors=2)
However I can run blocker just fine...
cd examples
python blocking.py myusername mypassword track
Output:
Cross the Line
Hi there,
This looks like an interesting project. I'm looking for alternatives to using libspotify and came across this. What is this project's status? Is it still being maintained? Is it still working? If not, can someone point me to a project with a similar approach?
Cheers
Hey @Hexxeh, just wanted to say a big Thank You! for this library. It's awesome :) And I've been diligently porting it to Node.js, and I'll be sure to show you that when it's ready :)
So my question is, have you figured out yet how to resole "file_id"s into (HTTP or otherwise) URIs? I'm trying to get album art for an album and can't figure out what to make of the Image
metadata type.
For example, when looking up the metadata of an "album" type, I see the "cover" field:
cover:
[ { fileId: <SlowBuffer 81 06 0a 6f 04 84 8d ad e7 ec 14 ee 72 27 2f 6c a1 e9 ff 5b>,
size: 'DEFAULT',
width: 300,
height: 300 },
{ fileId: <SlowBuffer cf 5a 0b 50 9b 35 66 25 4a db f8 1e d6 9e f8 f1 29 cb 0e 0b>,
size: 'SMALL',
width: 64,
height: 64 },
{ fileId: <SlowBuffer 13 51 85 85 82 75 a8 d1 91 d5 b5 7a 75 54 16 df 23 85 88 66>,
size: 'LARGE',
width: 640,
height: 640 } ],
Which gives me dimensions and an ID, but now I don't know how to make that into real image data. Any thoughts?
Cheers! And great work again on the lib!
Whenever I try to play a track Cloudfront tells me "Access Denied". I have the Unlimited plan and I am able to retrieve metadata about tracks. The url generated by the "play.py" example is "http://dsu0uct5x2puz.cloudfront.net/mp3/7731762c563cbd56a8af864f9924afe1e4465cea?Expires=1363303786&Signature=l7KXuAHYqNJbfEqEV0gKITZZ2hl-1kphLVSvA2IXDQ03nhOVL5k1rOEBdBk96YaS1uHGy~qIKLPb4Mv9f0~JXPnL3rrqgKWC0qf9yuILGtfmH2NprKAfKFj2uOCMH8nSEcSoN8jhT9zI9tqu7YdI2bMjA7hkD3gguW5YbJLZKi0_&Key-Pair-Id=APKAIJ6GQLKYCDRS5HFA".
I checked out the repo about an hour ago, so it should be up to date.
I get this when I search for a song that includes one or more from these letters: Å,Ä,Ö.
Search tracks: mamma sa
[1] Mamma sa - Maskinen
[2] Oh Mamma Sa - Radio - Adoo
Traceback (most recent call last):
File "respotify.py", line 340, in <module>
command_loop()
File "respotify.py", line 306, in command_loop
command_map[command_name][0](command[1:])
File "respotify.py", line 209, in command_search
set_current_playlist(results)
File "respotify.py", line 133, in set_current_playlist
display_playlist()
File "respotify.py", line 126, in display_playlist
print prefix + "[" + str(index) + "] " + track.getName() + " - " + track.getArtists(nameOnly=True)
UnicodeEncodeError: 'ascii' codec can't encode character u'\xe4' in position 8: ordinal not in range(128)
Would be great in someone know a easy fix. I'll look into it in a couple of days if no one succeeds and commit from a branch. Cheers!
Would you consider this feature request for you to add spotyxbmc2 as proof-of-concept Spotify binary addon for XBMC?
http://forum.xbmc.org/showthread.php?tid=67012
Arne Morten Kvarving (a.k.a. spiff, a.k.a. cptspiff) have forked XBMC and added binary addon support to it which is exacly what SpotyXBMC2 needs to run legally and be distributed precompiled, and he is looking for people to help him with his fork
Discussion about this with Spiff can be found here notspiff/kodi-cmake#1
The actual fork of XBMC with binary addon interface support can be found here
https://github.com/notspiff/xbmc-cmake
Above account on GitHub and source code is his even though he announced his fork with his old username
Hey there,
is there any Fifo implemented to control respotify from an external client?
I would like to be able to change the current playlist without quiting the local respotify-instance and start the external one..
Can anyone confirm this still works? Someone made a Chrome Extension and now they apparently patched something.
http://www.theverge.com/2013/5/8/4311606/spotify-patches-downloadify-chrome-exploit
The following is the trace. Any ideas how it could be fixed ?
In [1]: from spotify_web.spotify import SpotifyAPI
In [2]: sp = SpotifyAPI()
In [3]: sp.auth('uname', 'pwd')
Out[3]: True
In [4]: sp.metadata_request('spotify:track:4O2okbnHlCrU76QdtCE0Bl')
spotify:track:4O2okbnHlCrU76QdtCE0Bl
9df2018365cd4f0bb1575732b96a8bcb
AttributeError Traceback (most recent call last)
in ()
----> 1 sp.metadata_request('spotify:track:4O2okbnHlCrU76QdtCE0Bl')
/home/varun01124/code/spotify-websocket-api/spotify_web/spotify.pyc in metadata_request(self, uris, callback)
500 args = self.generate_multiget_args(SpotifyUtil.get_uri_type(uris[0]), mercury_requests)
501 print args
--> 502 return self.wrap_request("sp/hm_b64", args, callback, self.parse_metadata)
503
504 def toplist_request(self, toplist_content_type="track", toplist_type="user", username=None, region="global", callback=False):
/home/varun01124/code/spotify-websocket-api/spotify_web/spotify.pyc in wrap_request(self, command, args, callback, int_callback, retries)
467 if not callback:
468 for attempt in range(0, retries):
--> 469 data = WrapAsync(int_callback, self.send_command, command, args).get_data()
470 if data:
471 break
/home/varun01124/code/spotify-websocket-api/spotify_web/spotify.pyc in init(self, callback, func, _args)
63
64 self.data = False
---> 65 func(_args, callback=callback)
66
67 def callback(self, *args):
/home/varun01124/code/spotify-websocket-api/spotify_web/spotify.pyc in send_command(self, name, args, callback)
833 self.seq += 1
834
--> 835 self.send_string(msg)
836
837 def send_string(self, msg):
/home/varun01124/code/spotify-websocket-api/spotify_web/spotify.pyc in send_string(self, msg)
843 try:
844 with self.ws_lock:
--> 845 self.ws.send(msg_enc)
846 except SSLError:
847 Logging.notice("SSL error, attempting to continue")
AttributeError: 'NoneType' object has no attribute 'send'
do you have a link to the Java version ?
I get this when I want to list my playlists:
Traceback (most recent call last): File "respotify.py", line 285, in <module> command_loop() File "respotify.py", line 251, in command_loop command_map[command_name][0](command[1:]) File "respotify.py", line 91, in command_list print " ["+str(index)+"] "+playlist.getName() File "/usr/local/lib/python2.7/dist-packages/SpotifyWebsocketAPI-0.2-py2.7.egg/spotify_web/friendly.py", line 254, in getName return "Starred" if self.getID() == "starred" else self.obj.attributes.name AttributeError: 'bool' object has no attribute 'attributes'
spotify_web/spotify.py, line 192, should be "resp.json" without parens.
Line 199 should be "resp_json" instead of "resp.json()".
Are there any other ways to authenticate a user with Spotify other than a straight username/password? The developer website makes reference to OAuth but I'm not clear if that's something that would work here.
Hi Guys!!!
I think spotify modified the authentication a bit, but no big problem :)
I just had to change spotify.py line 187 into this rx = re.compile(""csrftoken":"(\w*)"")
Thanks 4 your work btw!!!
I try running the examples/play.py but it produces no output either in the terminal or from my speakers.
I have a premium account, and have tested that spotify-websocket-api works by getting several albums' metadata.
clients/respotify doesn't seem to work:
$ python respotify.py myusernae
Please enter your Spotify password
Traceback (most recent call last):
File "respotify.py", line 283, in
client.connect(host="localhost", port="6600")
File "/Library/Python/2.7/site-packages/mpd.py", line 491, in connect
self._sock = self._connect_tcp(host, port)
File "/Library/Python/2.7/site-packages/mpd.py", line 464, in _connect_tcp
raise err
socket.error: [Errno 61] Connection refused
I've tried adding 'print' statements to different functions within examples/play.py to see which blocks of code are running, but the only ones that show up are in the final if...else codeblock in play.py . There aren't even any error messages that show up, it just silently quits.
I've also tried changing the command play.py runs when it executes track_uri_callback() to something that I know works from the command line (with a known local file), but this still has no effect. Any ideas whats going on? Is this project out of date with the official spotify api? Or maybe I have too vague an understanding of what callbacks are?
A simple working example of retrieving a track's mp3 url would be extremely helpful.
On a macbook air:
$ uname -a
Darwin tmpname 13.0.0 Darwin Kernel Version 13.0.0: Thu Sep 19 22:22:27 PDT 2013; root:xnu-2422.1.72~6/RELEASE_X86_64 x86_64
$ python --version
Python 2.7.5
Just started playing with this API a couple days ago. At first, I was able to fetch mp3 urls with my Spotify free account. But, now I can't. I even created a new account and a new facebook account, linked them together, and tried with that account, but that doesn't work either. Did Spotify do something on their side to disable it?
According to the debian/copyright
you have a GPL3 license, you should probably add a COPYING or LICENSE file at the top level to make this more clear to people.
I've tried getting the metadata for certain songs and it just locks forever. E.g.
track = 'spotify:track:7FzURKB88npEvZ4Jzv0AEw'
track_obj = sp.metadata_request(track)
print track_obj
It never gets to the print statement. I don't know if this is an issue of the library or it's on the Spotify servers but we could perhaps have a timeout mechanism?
It looks like the streams here are 160kbps, but Spotify Premium can provide 320kbps streams. Is there any method to get those streams here?
The web client now has inbox support. Adding a reminder to add this - I may get a chance in the new year to implement this.
Am I doing something wrong? I'm seeing rate request errors when running the test.py here:
$ PASSWORD=**** USERNAME=**** ./test.py
[ERROR] Rate request error
.[ERROR] Rate request error
.
----------------------------------------------------------------------
Ran 2 tests in 14.271s
OK
I'm stunned by your project! Now I'm trying to rebuild it in Node.js, but already encounter a problem: https://github.com/Hexxeh/spotify-websocket-api/blob/master/spotify_web/spotify.py#L184
How do you find the secret? When I reuest the page I get two redirects and finally end up on Rick. What am I doing wrong?
[ERROR] There was a problem authenticating, no auth secret found
I find it very strange, that this error occurs just after yesterday. I was able to use your code without any problems, until now... When I manually request thr url in spotify_web/spotify.py:184 I can't find the secret myself.
Today 26-04-2013 I'm getting track errors for everithing. I have premium. Am I alone?
It seems the main library is broken somehow.
Running ./respotify results in this:
[NOTICE] Connecting to wss://lon2-linkap-a4.ap.spotify.com/
[NOTICE] Logging in
[DEBUG] sent {..... A lot of stuff.....}
[DEBUG] recv {"id":0,"result":"ok"}
[DEBUG] sent {"args":[],"name":"sp/user_info","id":"1"}
[DEBUG] recv {"message":["do_work","var t2; try{ var d=new Date(); t2=d[(35725129862..toString(36<<0))[0]];}catch() {};this.reply('' + t2);"]}
[DEBUG] Got do_work message, payload: var t2; try{ var d=new Date(); t2=d[(35725129862..toString(36<<0))[0]];}catch() {};this.reply('' + t2);
[DEBUG] sent {"args":["v1"],"name":"sp/work_done","id":"2"}
[DEBUG] recv {"message":["ping_flash2","90 177 209 213 40 76 89 196 90 9 254 253 7 248 109 220 81 91 1 85"]}
[DEBUG] Sending pong 200 38 246 72 76 145 84 44 143 73
[DEBUG] sent {"args":["200 38 246 72 76 145 84 44 143 73"],"name":"sp/pong_flash2","id":"3"}
[DEBUG] recv {"id":2,"result":"ok"}
[DEBUG] Got ack for message reply
Traceback (most recent call last):
File "./respotify.py", line 277, in
spotify = Spotify(args.username, args.password)
File "../../spotify_web/friendly.py", line 619, in init
self.tunigo = Tunigo(region=self.api.country)
File "../../spotify_web/tunigoapi.py", line 14, in init
Logging.debug("Starting with Tunigo for region: " + self.region)
TypeError: cannot concatenate 'str' and 'NoneType' objects
Running ./blocking.py :
.....
[DEBUG] Got ack for message reply
Traceback (most recent call last):
File "./blocking.py", line 40, in
print album.name+" - "+album.artist[0].name+"\n"
AttributeError: 'bool' object has no attribute 'name'
All the other examples fail too.
Sincerly,
Dominik Thalhammer
I've started to see UTF-8 decoding exceptions being thrown by the google protobuf library when parsing Spotify responses:
Example stack trace below:
File "../spotify_web/spotify.py", line 249, in parse_metadata
header.ParseFromString(base64.decodestring(resp[0]))
File "/usr/local/Cellar/python/2.7.3/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/google/protobuf/message.py", line 179, in ParseFromString
self.MergeFromString(serialized)
File "/usr/local/Cellar/python/2.7.3/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/google/protobuf/internal/python_message.py", line 755, in MergeFromString
if self._InternalParse(serialized, 0, length) != length:
File "/usr/local/Cellar/python/2.7.3/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/google/protobuf/internal/python_message.py", line 782, in InternalParse
pos = field_decoder(buffer, new_pos, end, self, field_dict)
File "/usr/local/Cellar/python/2.7.3/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/google/protobuf/internal/decoder.py", line 410, in DecodeField
field_dict[key] = local_unicode(buffer[pos:new_pos], 'utf-8')
UnicodeDecodeError: 'utf8' codec can't decode byte 0x8d in position 13: invalid start byte
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.