Giter VIP home page Giter VIP logo

mopidy-musicbox-webclient's Introduction

Mopidy-MusicBox-Webclient

Latest PyPI version CircleCI build status Test coverage JavaScript Standard Style

Mopidy MusicBox Webclient (MMW) is a frontend extension and JavaScript-based web client especially written for Mopidy.

Features

  • Responsive design that works equally well on desktop and mobile browsers.
  • Browse content provided by any Mopidy backend extension.
  • Add one or more tracks or entire albums to the queue.
  • Save the current queue to an easily accessible playlist.
  • Search for tracks, albums, or artists from specific backends or all of Mopidy.
  • Shows detailed track and album information during playback, with album cover retrieval from Last.fm.
  • Support for all of the Mopidy playback controls (consume mode, repeat, shuffle, etc.)
  • Deep integration with, and additional features for, the Pi MusicBox.
  • Fullscreen mode.

https://github.com/pimusicbox/mopidy-musicbox-webclient/raw/develop/screenshots/overview.png

Dependencies

  • MMW has been tested on the major browsers (Chrome, IE, Firefox, Safari, iOS). It may also work on other browsers that support websockets, cookies, and JavaScript.
  • Mopidy >= 3.0.0. An extensible music server that plays music from local disk, Spotify, SoundCloud, Google Play Music, and more.

Installation

Install by running:

sudo python3 -m pip install Mopidy-MusicBox-Webclient

Or, if available, install the Debian/Ubuntu package from apt.mopidy.com.

Configuration

MMW is shipped with default settings that should work straight out of the box for most users:

[musicbox_webclient]
enabled = true
musicbox = false
websocket_host =
websocket_port =
on_track_click = PLAY_ALL

The following configuration values are available should you wish to customize your installation further:

  • musicbox_webclient/enabled: If the MMW extension should be enabled or not. Defaults to true.
  • musicbox_webclient/musicbox: Set this to true if you are connecting to a Mopidy instance running on a Pi Musicbox. Expands the MMW user interface to include system control/configuration functionality.
  • musicbox_webclient/websocket_host: Optional setting to specify the target host for Mopidy websocket connections.
  • musicbox_webclient/websocket_port: Optional setting to specify the target port for Mopidy websocket connections.
  • musicbox_webclient/on_track_click: The action performed when clicking on a track. Valid options are: PLAY_ALL (default), PLAY_NOW, PLAY_NEXT, ADD_THIS_BOTTOM, ADD_ALL_BOTTOM, and DYNAMIC (repeats last action).

Usage

Enter the address of the Mopidy server that you are connecting to in your browser (e.g. http://localhost:6680/musicbox_webclient)

Project resources

Credits

mopidy-musicbox-webclient's People

Contributors

andrenarchy avatar camilonova avatar davisnt avatar jaakkosipari avatar jcass77 avatar jodal avatar joelpet avatar kingosticks avatar randombyte avatar rysaunders avatar smgt avatar szimek avatar wagamama avatar woutervanwijk avatar xiongyihui avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

mopidy-musicbox-webclient's Issues

Search seems selective on Spotify

I'm not sure what it is, but I've installed your HTTP client, which works well for existing playlists and to control Mopidy, however the search function seems odd.

If I search for "Beyonce" I get dozens of results under artist and Albums, however if I search "Skrillex" I find nothing. Does Mopidy just dislike my taste in music?

Support consume playback mode

Add button on the now playing footer to control consume mode.

Also be nice to:

  • Have tooltips for all the buttons (only shuffle currently has one)
  • Make it more obvious which playback modes are currently enabled

"Settings" menu option gives 404

I think the title says enough :)

The left-hand side menu option "Settings" points to http;//host_name/Settings, and gives a 404. Browsing manually to http://host_name/settings (lower-case 'S') gives me a page that looks malformed.

I cloned the repository (web client folder) last Tuesday Sept. 2nd.

Webclient broken if placed behind reverse proxy

In my setup, the mopidy server is made available through a reverse proxy (nginx) such that the mopidy root is available under https://domain/mopidy, i.e., the web clients are available as

  • https://domain/mopidy/musicbox_webclient/
  • https://domain/mopidy/moped/
  • ...

The webclient fails to start up because it looks for https://domain/mopidy/mopidy.min.js while it should load https://domain/mopidy/mopidy/mopidy.min.js.

The fix is to load the js file through the relative path ../mopidy/mopidy.min.js instead of /mopidy/mopidy.min.js. I'll prepare a PR in a second.

Add support for Soundcloud

Hi Wouter,

It'd be great to add support for Soundcloud.

Currently, the client pulls in the playlists on load but they are inaccessible from the interface, it just adds the loading wheel and doesn't seem to trigger any requests or errors.

See: screen shot 2013-09-18 at 9 44 42 am

Let me know if you have any plans for adding robust support for https://github.com/dz0ny/mopidy-soundcloud

I would be happy to contribute if neccessary.

Cheers,

Ability to remove radio stations

Wanted the ability to remove radio stations from the list without manually editing the cookie read in with the array.

Did this in the develop branch and pushed it to my pi and it works.

function removeRadioUri(name) {
        toast('Removing radiostation...');

        // remove radio station from list
        document.activeElement.blur();
        $("input").blur();

        for (var key in radioStations) {
            rs = radioStations[key];
            if (null!=rs && rs[0]==name) {
                delete radioStations[key];
                continue;
            }
        }
        saveRadioStations();
        updateRadioStations();
}

function updateRadioStations() {
    var tmp = '';
    $('#radiostationstable').empty();
    var child = '';
    for (var key in radioStations) {
        var rs = radioStations[key];
        if (rs) {
            name = rs[0] || rs[1];
            child = '<li><a href="#" onclick="return addRadioUri(\'' + rs[0] + '\', \'' + rs[1] + '\');">';
            child += '<h1>' + name + '</h1></a><a href="#" onclick="return removeRadioUri(\'' + rs[0] + '\');">Remove</a></li>';
            tmp += child;
        }
    }
    ;
    $('#radiostationstable').html(tmp);
}

more play options in popup

Ideas to make the player more flexible:

  • add single track to queue
  • play single track
  • add album to queue

404 on settings page

Im getting a 404 on the settings page and most other pages (with the exception of radio page) appear blank. The web-radio playback function, volume control and play/pause seem to work perfectly. All other pages remain blank or non-responsive.

System info:
Pi model B running 2014-06-20-wheezy-raspbian
Mopidy 0.18.3 installed via apt
Symlink to "usr/share/mopidy/mopidy/http/data" created.

Browser(s): Chrome 35.0.1916.153 and Firefox 30 on OSX

Let me know if there's any other info I can provide on the setup.

Text from 404 error (settings page):
404 Not Found
The path '/Settings' was not found.
Traceback (most recent call last):
File "/usr/lib/python2.7/dist-packages/cherrypy/_cprequest.py", line 656, in respond
response.body = self.handler()
File "/usr/lib/python2.7/dist-packages/cherrypy/lib/encoding.py", line 188, in call
self.body = self.oldhandler(_args, *_kwargs)
File "/usr/lib/python2.7/dist-packages/cherrypy/_cperror.py", line 386, in call
raise self
NotFound: (404, u"The path '/Settings' was not found.")
Powered by CherryPy 3.2.2

support both local music and Spotify search

Would it be possible to make the search function of the webclient such that it searches both the locally stored music and spotify? I configured mopidy to use local data as well, and mpdroid finds both the local and Spotify data.

Can't remove Spotify trackers from the queue

When trying to remove a Spotify song from the queue I'm getting the following error:

"Filter values must be iterable: u'spotify:track:71e8zPwS2t9om1lGY35NS6'"

"Traceback (most recent call last):
  File "/usr/lib/python2.7/site-packages/mopidy/utils/jsonrpc.py", line 130, in _handle_single_request
    result = self._unwrap_result(result)
  File "/usr/lib/python2.7/site-packages/mopidy/utils/jsonrpc.py", line 219, in _unwrap_result
    result = result.get()
  File "/usr/lib/python2.7/site-packages/pykka/future.py", line 299, in get
    exec('raise exc_info[0], exc_info[1], exc_info[2]')
  File "/usr/lib/python2.7/site-packages/pykka/actor.py", line 200, in _actor_loop
    response = self._handle_receive(message)
  File "/usr/lib/python2.7/site-packages/pykka/actor.py", line 294, in _handle_receive
    return callee(*message['args'], **message['kwargs'])
  File "/usr/lib/python2.7/site-packages/mopidy/core/tracklist.py", line 387, in remove
    tl_tracks = self.filter(criteria, **kwargs)
  File "/usr/lib/python2.7/site-packages/mopidy/core/tracklist.py", line 334, in filter
    raise ValueError('Filter values must be iterable: %r' % values)
ValueError: Filter values must be iterable: u'spotify:track:71e8zPwS2t9om1lGY35NS6'

soundcloud compatibility?

Hi, and thanks for the great client!

In my installation, with the soundcloud extension installed, the webclient gets stuck on "Loading data from MusicBox" when I select a playlist from soundcloud.

Maybe this is an issue with the soundcloud plugin, but I thought I'd ask here first. I have the same problem with the Ario mpd client on Windows, but soundcloud playlists work fine on gmpc on Windows and mpdroid on Android (with a long enough timeout).

I run mopidy 0.14.1 on the latest raspbian wheezy image (2013-02-09), and only configured static_dir in mopidy.conf to point to the webclient. Could I be missing something else? Or is soundcloud simply not supported/tested yet?

I am not a coder nor a linux expert, but if there's a way I could help with more info, I'd be happy to.

EDIT -- more info: If I play a soundcloud playlist (via another client), the web client shows the title info at the top. Next and Previous buttons also work, but the queue shows up empty.

Jukebox/Party Mode

It'll be cool if there was a good support for multiple users.

It'll be great to allow anyone in the office to search Spotify & queue the songs on the juke box using this web UI.

Avoid polling for current track and time changes when not playing.

Mopidy will let you know over the WS when the state changes, so you should be able to get rid of the polling. Ideally you check this type of state once the connection is up to get an initial state, and then after that you just let us emit events to you when things change.

Only exception to this might be that you still want to fetch the time every now and then while playing to avoid drifting, as won't haven't figured out a better solution for this quite yet.

Invalid RPC request to display Playlists

Software Used

  • Mopidy 0.11.1 (Raspberry PI, ARMv6 HardFloat Libspotify)
  • Mopidy-Webclient (751bdb5)
  • Google Chrome 24.0.1312.57 m
  • Raspbian (Debian Wheezy ARM Hardfloat)

Report

The playlists fail to load with the following message:
Chrome Web Developer Console:

Server returned error: 
Object
code: -32602
data: Object
message: "Invalid params"
__proto__: Object

Mopidy error:

"get_playlists() takes exactly 1 argument (2 given)"
 "Traceback (most recent call last):
  File "/usr/lib/pymodules/python2.7/mopidy/utils/jsonrpc.py", line 130, in _handle_single_request
    result = self._unwrap_result(result)
  File "/usr/lib/pymodules/python2.7/mopidy/utils/jsonrpc.py", line 219, in _unwrap_result
    result = result.get()
  File "/usr/lib/pymodules/python2.7/pykka/future.py", line 116, in get
    'raise exc_info[0], exc_info[1], exc_info[2]')
  File "/usr/lib/pymodules/python2.7/pykka/actor.py", line 201, in _actor_loop
    response = self._handle_receive(message)
  File "/usr/lib/pymodules/python2.7/pykka/actor.py", line 284, in _handle_receive
    return callee(*message['args'], **message['kwargs'])
TypeError: get_playlists() takes exactly 1 argument (2 given)
"

Fix

Current (Library.js , rules 94-101)

/*********************************
 * Playlists
 *********************************/

function getPlaylists() {
    //  get playlists without tracks
    mopidy.playlists.getPlaylists(false).then(processGetPlaylists, console.error);
}

Fix:
Remove false as argument of getPlayLists()

/*********************************
 * Playlists
 *********************************/

function getPlaylists() {
    //  get playlists without tracks
    mopidy.playlists.getPlaylists().then(processGetPlaylists, console.error);
}

Add a way to edit the queue

It would be nice if the queue could be edited. For example by creating a popup menu for the queue to (re)move tracks

Assumes Mopidy websocket on port 6680

There is an assumption that Mopidy's HTTP interface is exposed on port 6680 but the user may have a non-standard Mopidy HTTP configuration. If this is the case then the user sees the message "Trying to reach MusicBox. Please wait...".

For the port, Mopidy-Webclient should first off use what the user has specified in the address bar, if nothing was specified then 6680 could be used by default. Ideally the default port could be somehow configurable.

Keyboard shortcut blocking

Currently, MusicBox web interface blocks all keyboard shortcuts (like ctrl+tab, alt+w etc).

Expected behaviour would be only blocking these keyboard actions, that MusicBox overrides (ie space for pause/play).

Fix:
@mopidy_musicbox_webclient/static/js/gui.js:509

$(document).keypress( function (event) {
    //console.log('kp: '+event);
    if (event.target.tagName != 'INPUT') {
-       event.preventDefault();
        switch(event.which) {
            case 32:
                doPlay();
+               event.preventDefault();
                break;
            case '>':
                doNext();
+               event.preventDefault();
                break;
            case '<':
                doPrevious();
+               event.preventDefault();
                break;
        }
    return true;
    }
});

Stop button

Hi,
would it be possible to add a "stop button" to the Web interface instead of/in addition to the pause button? I'm not a huge fan of the pause button, as I'm having trouble continuing playing music where I left off (lets say a couple of days ago). The stream simply won't continue playing, and the whole web interface seems rather unresponsive after a while (happens with spotify, and also with the audioaddict addon).This wouldn't happen if I would be able to stop the stream instead of pausing it. I'm using Pi Musicbox 0.5.1beta, but the problem exists in older versions as well.

Update interface for easier use of different services

The webinterface would be better if you could browse/search within different services (mopidy extensions), like Spotify, Google Music, Local, Podcasts, etc

Liket this: https://fbcdn-sphotos-g-a.akamaihd.net/hphotos-ak-xfp1/v/t1.0-9/p180x540/1974986_686711361368359_8019291498242019323_n.png?oh=ff77f892ecd7941d36ed8ac3c2d79f9a&oe=54CB3183&__gda__=1417895665_057dc9823ed44861ed6b5d6b320f76c8

Also Dirble/Tunein/etc should be integrated in the Radio part

HandshakeError: Header Upgrade is not defined

Just setup a brand new client install, after starting and attempting to visit the MusicBox url/location, I get the spinning wheel Trying to reach MusicBox. Please wait...
image

As well, my console for mopidy logs the following:

ERROR    [19/Nov/2013:08:50:47]  Traceback (most recent call last):
  File "/usr/local/lib/python2.7/dist-packages/cherrypy/_cprequest.py", line 102, in run
    hook()
  File "/usr/local/lib/python2.7/dist-packages/cherrypy/_cprequest.py", line 62, in __call__
    return self.callback(**self.kwargs)
  File "/usr/lib/python2.7/dist-packages/ws4py/server/cherrypyserver.py", line 130, in upgrade
    raise HandshakeError('Header %s is not defined' % key)
HandshakeError: Header Upgrade is not defined

ERROR    [19/Nov/2013:08:50:47] HTTP Traceback (most recent call last):
  File "/usr/local/lib/python2.7/dist-packages/cherrypy/_cprequest.py", line 647, in respond
    self.hooks.run('before_request_body')
  File "/usr/local/lib/python2.7/dist-packages/cherrypy/_cprequest.py", line 112, in run
    raise exc
HandshakeError: Header Upgrade is not defined

Other info:

Config File: https://gist.github.com/kenetik/7545719
Dependencies List: https://gist.github.com/kenetik/7545814
Debug Log: https://gist.github.com/kenetik/7545906
Distro:

Distributor ID: Ubuntu
Description:    Ubuntu 12.04.3 LTS
Release:        12.04
Codename:       precise

Browsing Google Music library

Hello,

I was looking for some web-frontend that would implement browsing and was really glad someone pointed me to the dev tree of you webclient. It seems browsing only works for local music? I cannot see anything from my Google Music library.

Any chance this will become possible?

Many thanks!
Chris

Support Spotify playlist folder structure

First of all: thanks for all the good work, I use it a lot. I would like to propose the following enhancement as I structure my Spotify playlists using folders. Is it possible to support a foldable folder structure in the webclient? Personally I would like to have the folders unfold by default.

Search on spotify doesn't work

Seems that I cannot search on Spotify. This is the only backend I have enabled on mopidy.. I enter any query into the search box, and the webclient just sits there forever with the 'Loading data from Musicbox. Please wait' dialog.

Searching works with MPDroid and the Gnome Music Player client.

Allow scrolling playlists and playlist contents independently

If you have more playlists than can be displayed and scroll down to select one at the bottom, you then need to scroll back up to the top to see it's contents. Ideally the list of playlists and the list of tracks in the selected playlist could be scrolled independently .

Update to Mopidy 0.16.x Core API

Due to the updated Core API in Mopidy 0.16.x the webclient's library/playlist does not work anymore.

v0.16.0 (2013-10-27)
In addition, we’ve seen some cleanup to the playback vs tracklist part of the core API, which will require some changes for users of the HTTP/JavaScript APIs, as well as the addition of audio muting to the core API.

Parts of the functionality in mopidy.core.PlaybackController have been moved to mopidy.core.TracklistController:

refresh playlists

When the playlist is refreshed on spotify, it's not reflected in the webclient.

No Sound

hi, any troubleshooting guidance for no sound? using these settings in mopidy.conf

[audio]
mixer = software
mixer_volume = 75
output = autoaudiosink

visualizer =

Volume levels do not persist from song to song

So I've set this web client up in my office and it's been mostly great so far, except that when we lower the volume on a song, it does not persist this setting to the next one. This leaves us babysitting the GUI instead of getting back to work. A quick hack would be to lower the output volume on the speakers themselves, but that's not a real solution. Worst part is, the GUI doesn't show this increase to 100 when the song changes, so you have to nudge it up or down to get it back down to normal.

Edit: running Mopidy-MusicBox-Webclient (1.0.1) currently.

Git clone not working on a rasbian install

Hi,

I tried to make a git clone of the client but it fails on a standard raspbian installation with mopidy. I think it's because radiostations.js points to a non existing location.
I hope you can ifx that!

Thanks!
Merijn

Open single quote in index.html

On line 380 there is s single quote after buttons.

this breaks the WEB GUI. Please remove it. I do have already fixed in my local branch.

Make the web client a Mopidy extension

The shortly upcoming Mopidy 0.19 release lets web clients be installed as Mopidy extensions. This makes it possible to install a web client simply by running e.g. pip install Mopidy-Lux and restarting Mopidy. No configuration changes needed. No git clone or download-and-unzip.

Multiple web clients can be installed at the same time, as each client is hosted with a prefix, e.g. Mopidy-Lux on http://localhost:6680/lux/. Mopidy provides a list of all installed web clients on http://localhost:6680/mopidy/.

Mopidy 0.19 hasn't been released yet, but this feature is complete and available in our develop branch. We want web client authors to start working on making all the web clients pip-installable.

The main sources of information is:

We'll be happy to help you get this working nicely. Let us know if you run into problems.

Click on URI is broken for URIs with ? in

I've been messing around making a better local backend for mopidy. One key thing is to return albums and artists in search results, and return sensible browse URIs for them, so you can click on the album name and go to the album.

Now initially, I used the same URIs as in mopidy-local-sqlite for compatibility,
eg. local:directory?type=artist

However, if you return a URI like this as the URI for an album or artist in the search results, it appears not to work, I had to change them to local:directory/artist/ instead. I suspect there is a quoting issue somewhere here.

Sound slider does not work

Changeing the volume by using the Volume Slider in the track view, just mutes all sound.
( non-musicbox installation, Rasbian)

edit: local config issue

Browsing folders - problem with umlaut

In the latest version of the Pi MusicBox (0.5.2), I'm not able to browse the folders of my network share via the web interface ('Browse/Local media/Folders/Network'). It results in 'No tracks found...'.

The network share is mounted correctly (check via ssh, search function and browse by artist). The folders and files are available to the system but simply not shown in the browse by folder function in the web interface. The issue seems to be related with german umlauts (ä,ö,ü). As soon as a single folder contains one of those characters, the browse by folder function does not work anymore.

Clearing the queue also deletes current playing track

Hi!

When clearing the queue also the current playing track is stopped. I would prefer to keep the current song playing while putting new music into the queue. What do you think?

I use the Musicbox 0.5.1 RC2. I like this version a lot!

Problem with fullscreen mode

Hey,

I think there is a bug in the online user interface. In fullscreen mode I can't scrool, even if the list of tracks or something like this is longer as the viewpoints height.

I hope u wil fix it.

Cannot open Settings page with non-ASCII characters in Spotify username

Hi,

I am running the new beta version of the MusicBox on my Raspberry Pi and can use the web interface just fine, but when I click on 'Settings', I get the message

500 Internal Server Error

The server encountered an unexpected condition which prevented it from fulfilling the request.

Traceback (most recent call last):
  File "/usr/lib/python2.7/dist-packages/cherrypy/_cprequest.py", line 656, in respond
    response.body = self.handler()
  File "/usr/lib/python2.7/dist-packages/cherrypy/lib/encoding.py", line 188, in __call__
    self.body = self.oldhandler(*args, **kwargs)
  File "/usr/lib/python2.7/dist-packages/cherrypy/_cpdispatch.py", line 34, in __call__
    return self.callable(*self.args, **self.kwargs)
  File "/usr/local/lib/python2.7/dist-packages/mopidy/http/actor.py", line 197, in Settings
    return template.render ( templateVars )
  File "/usr/local/lib/python2.7/dist-packages/jinja2/environment.py", line 969, in render
    return self.environment.handle_exception(exc_info, True)
  File "/usr/local/lib/python2.7/dist-packages/jinja2/environment.py", line 742, in handle_exception
    reraise(exc_type, exc_value, tb)
  File "/opt/webclient/settings/index.html", line 186, in top-level template code
    <input type="password" name="spotify__password" value="{{ spotify__password }}" size="10" maxlength="40"/>
UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position 4: ordinal not in range(128)

Powered by CherryPy 3.2.2 

I have a non-ASCII character (ö) in my username, but not in my password. Since I cannot change my Spotify username, I cannot work around this at the moment. When I remove my username from the settings.ini file, the settings page loads, but then I am not connected to Spotify.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.