Skip to content
This repository has been archived by the owner on May 3, 2019. It is now read-only.

Plex plugin: initial bring up #135

Merged
merged 17 commits into from
Nov 5, 2017
Merged

Plex plugin: initial bring up #135

merged 17 commits into from
Nov 5, 2017

Conversation

Cogitri
Copy link
Contributor

@Cogitri Cogitri commented Oct 20, 2017

The initial bring up for the Plex integration. This is currently a work-in-progess.

Things which have to be fixed:

  • Fix media controls
  • Fix media art
  • Update song title
  • It needs a copyright header, I guess - wasn't quite sure what it should look like
  • General coding style? Still a JS novice

Also: Since plex servers can be hosted locally it would be nice if one had the ability to set a custom IP for this plugin

Currently everything but the media keys and art are working
@coveralls
Copy link

Coverage Status

Coverage remained the same at 92.746% when pulling 7e844a2 on Cogitri:develop into fd7b651 on ColinDuquesnoy:develop.

@Cogitri
Copy link
Contributor Author

Cogitri commented Oct 20, 2017

Since the last commit artUrl point to a blob object of the Image. However, it still isn't displayed correctly. Currently artUrl looks like this: blob:http://192.168.1.72:32400/9f45e12c-2917-4457-8cd5-c999fb4180b1 (In this case 192.168.1.72 because it's my local server). This works fine in my browser, but MellowPlayer doesn't display the correct image. However, converting a blob to an actual URL seems like a hassle

About the media keys: I'm not exactly sure why those don't work. The current clickButton function does detect the correct buttons ( I guess ), however, click-ing on them doesn't do anything

@coveralls
Copy link

Coverage Status

Coverage remained the same at 92.746% when pulling 3a17c68 on Cogitri:develop into fd7b651 on ColinDuquesnoy:develop.

@ColinDuquesnoy
Copy link
Owner

Currently artUrl looks like this: blob:http://192.168.1.72:32400/9f45e12c-2917-4457-8cd5-c999fb4180b1 (In this case 192.168.1.72 because it's my local server). This works fine in my browser, but MellowPlayer doesn't display the correct image. However, converting a blob to an actual URL seems like a hassle

If I understood right, blob url are in memory resources than can only be accessed from within the browser. I'd suggest to convert the image blob url to a base64 string (https://stackoverflow.com/questions/18650168/convert-blob-to-base64), that should work if the blob url wasn't revoked.

General coding style? Still a JS novice

That looks OK. You can use jsbeautifier to format your js code.

Since plex servers can be hosted locally it would be nice if one had the ability to set a custom IP for this plugin

Users can change a service URL by going to Settings > Services > Click on URL.

About the media keys: I'm not exactly sure why those don't work. The current clickButton function does detect the correct buttons ( I guess ), however, click-ing on them doesn't do anything

Weird. Does it work if you run the same code in your browser's javascript console?

@ColinDuquesnoy
Copy link
Owner

It needs a copyright header, I guess - wasn't quite sure what it should look like

That is not stricly necessary (but I am not a license expert). If you feel like you a want to put a copyright header, you can use the following:

//-----------------------------------------------------------------------------
//
// This file is part of MellowPlayer.
//
// MellowPlayer is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// MellowPlayer is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with MellowPlayer.  If not, see <http://www.gnu.org/licenses/>.
//
//-----------------------------------------------------------------------------

@Cogitri
Copy link
Contributor Author

Cogitri commented Oct 21, 2017

Weird. Does it work if you run the same code in your browser's javascript console?

Oh, sorry if that wasn't clear, it doesn't work in my browser either. I'm pretty sure that it's the correct element though, it's just that .click() doesn't do anything. Not sure how to debug that, especially with Plex' minified JS code

@ColinDuquesnoy
Copy link
Owner

Not sure how to debug that, especially with Plex' minified JS code

You can un-minify javascript from chrome/chromium (Chrome inspector -> Sources tab -> { }) or use http://unminify.com/.

@Cogitri
Copy link
Contributor Author

Cogitri commented Oct 23, 2017

You can un-minify javascript from chrome/chromium (Chrome inspector -> Sources tab -> { }) or use http://unminify.com/.

Ah, thanks.

The last commit converts the blob to a base64 encoded image, however, it still doesn't work :/

@Cogitri
Copy link
Contributor Author

Cogitri commented Oct 23, 2017

Okay, thanks to a code snippet from Nuvola player everything but media art works now. Thanks for sending me that link!
I wasn't quite sure how to do the copyright header, now that I've taken code from a BSD-2 licensed project, but the GPL and BSD-2 seem to be compatible, so I just included both.
About the media art: When I run the commands of the integration script in my browser it works fine, with MellowPlayer I get this from time to time:
XMLHttpRequest cannot load data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7. Invalid response. Origin 'https://app.plex.tv' is therefore not allowed access.

@coveralls
Copy link

Coverage Status

Coverage increased (+0.002%) to 92.748% when pulling a4d330c on Cogitri:develop into fd7b651 on ColinDuquesnoy:develop.

@Cogitri
Copy link
Contributor Author

Cogitri commented Oct 23, 2017

I'm still not quite happy with the color scheme, the font is kind of hard to read currently, but with plex' accent color as background color it looks pretty terrible. Maybe you know something better?

@coveralls
Copy link

Coverage Status

Coverage remained the same at 92.748% when pulling e633080 on Cogitri:develop into c8e8989 on ColinDuquesnoy:develop.

@coveralls
Copy link

Coverage Status

Coverage remained the same at 92.748% when pulling c572dac on Cogitri:develop into c8e8989 on ColinDuquesnoy:develop.

@coveralls
Copy link

Coverage Status

Coverage remained the same at 92.748% when pulling c572dac on Cogitri:develop into c8e8989 on ColinDuquesnoy:develop.

@ColinDuquesnoy
Copy link
Owner

I'm still not quite happy with the color scheme, the font is kind of hard to read currently, but with plex' accent color as background color it looks pretty terrible. Maybe you know something better?

I've tried the theme, I just had to change the foreground color to #cbcbcb and it looks OK. Changing the background color to a darker color such as #1f1f1f make it looks even better 😉

I wasn't quite sure how to do the copyright header, now that I've taken code from a BSD-2 licensed project, but the GPL and BSD-2 seem to be compatible, so I just included both.

Fine 👍

About the media art: When I run the commands of the integration script in my browser it works fine, with MellowPlayer I get this from time to time:

What if you don't use an XMLHttpRequest? If I understood right, a blob url should point to a binary blob in memory and no http request should be necessary.

Also, next time, run MellowPlayer with --log-level 1, add some console.log before and after you transform the blob url and paste the relevant logs here.

@Cogitri
Copy link
Contributor Author

Cogitri commented Oct 25, 2017

Also, next time, run MellowPlayer with --log-level 1, add some console.log before and after you transform the blob url and paste the relevant logs here.

[D] [2017-10-25 14-18-57:554459] [Player-Plex] playback status changed: 2
[D] [2017-10-25 14-18-57:554721] [Notifier] notification disabled: Plex - Paused
[D] [2017-10-25 14-18-57:554933] [Player-Plex] song changed: EastNewSound - Spoiler by 
[D] [2017-10-25 14-18-57:555550] [AlbumArtDownloader] creating base64 image from data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7 to /home/rasmus/.cache/MellowPlayer/MellowPlayer/Covers/0
[D] [2017-10-25 14-18-57:555582] [root] decoding image: data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7
[D] [2017-10-25 14-18-57:555711] [root] image format: R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7
[D] [2017-10-25 14-18-57:555729] [root] image data: R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7
[D] [2017-10-25 14-18-57:556435] [root] image saved to: /home/rasmus/.cache/MellowPlayer/MellowPlayer/Covers/0

So it does create a thumbnail when I don't do a XMLHttpRequest, however the humbnail is just one white pixel. Seems like the base64 string for the thumbnail isn't correct, even though it works fine if I run the code directly in chromium and open the blob url

@ColinDuquesnoy
Copy link
Owner

Yes the base64 string looks truncated (you may want to use https://codebeautify.org/base64-to-image-converter to check if the string is valid).

What if you use the same technique as in the nuvolaplayer plugin? ( https://github.com/tiliado/nuvola-app-plex/blob/master/integrate.js#L146 )

@Cogitri
Copy link
Contributor Author

Cogitri commented Oct 25, 2017

What if you use the same technique as in the nuvolaplayer plugin? ( https://github.com/tiliado/nuvola-app-plex/blob/master/integrate.js#L146 )

As far as I can tell Nuvola's plex plugin doesn't work anymore

@Cogitri
Copy link
Contributor Author

Cogitri commented Oct 25, 2017

Okay, it seems like MellowPlayer actually can't download the image via the blob URL and the catch statement just hides that currently

[D] [2017-10-25 19-59-04:930331] [Player-Plex] play()
[D] [2017-10-25 19-59-05:009444] [Player-Plex] playback status changed: 1
[D] [2017-10-25 19-59-05:009742] [Notifier] notification disabled: Plex - The Battle Anthem by phase trax records
[E] [2017-10-25 19-59-05:205867] [js] Refused to get unsafe header "Location"
[D] [2017-10-25 20-01-01:619056] [AlbumArtDownloader] downloading blob:http://192.168.1.72:32400/6e47a02a-352e-4566-9951-a1a5ddea93c9 to /home/rasmus/.cache/MellowPlayer/MellowPlayer/Covers/0
[D] [2017-10-25 20-01-01:619095] [FileDownloader] downloading blob:http://192.168.1.72:32400/6e47a02a-352e-4566-9951-a1a5ddea93c9 to /home/rasmus/.cache/MellowPlayer/MellowPlayer/Covers/0
[D] [2017-10-25 20-01-01:619570] [FileDownloader] download failed: Protocol "blob" is unknown
[D] [2017-10-25 20-01-01:619590] [AlbumArtDownloader] download finished

@ColinDuquesnoy
Copy link
Owner

Yes that's normal, you cannot get data from a blob url using an http GET request (see https://stackoverflow.com/questions/30864573/what-is-a-blob-url-and-why-it-is-used). You have to read the blob url from withing the browser using javascritp for that to work.

Something like:

var reader = new window.FileReader();
reader.readAsDataURL(blob); 
reader.onloadend = function() {
    base64data = reader.result;                
    console.log(base64data );
}

I'd suggest you try this in your browser and use https://codebeautify.org/base64-to-image-converter to check if the base64 string is valid. If it is valid and MellowPlayer cannot decode/download the image from the same base64 string, then that is a bug.

@coveralls
Copy link

Coverage Status

Coverage remained the same at 92.748% when pulling 5f46382 on Cogitri:develop into c8e8989 on ColinDuquesnoy:develop.

@Cogitri
Copy link
Contributor Author

Cogitri commented Oct 25, 2017

You have to read the blob url from withing the browser using javascritp for that to work.

Okay, thought MellowPlayer could read it directly from the blob by reading the file directly from its memory,

a blob url should point to a binary blob in memory and no http request should be necessary.

suggested that to me.

var reader = new window.FileReader();
reader.readAsDataURL(blob);
reader.onloadend = function() {
base64data = reader.result;
console.log(base64data );
}

I don't think this will work with just a blob url though, after a bit of googling a4d330c seems like the only approach which works with a blob url. However, that doesn't seem to work with QtWebengine/MellowPlayer ( even though it does with chromium )

@coveralls
Copy link

Coverage Status

Coverage decreased (-0.06%) to 92.69% when pulling aa8c341 on Cogitri:develop into c8e8989 on ColinDuquesnoy:develop.

@Cogitri
Copy link
Contributor Author

Cogitri commented Oct 29, 2017

I fixed this in commit 28f444c

Awesome, I just applied the patch to 3.1 and it works now!

@coveralls
Copy link

Coverage Status

Coverage decreased (-0.1%) to 92.638% when pulling aa8c341 on Cogitri:develop into c8e8989 on ColinDuquesnoy:develop.

@ColinDuquesnoy
Copy link
Owner

Awesome, I just applied the patch to 3.1 and it works now!

Great! Let me know when you think the PR is ready to merge.

@coveralls
Copy link

Coverage Status

Coverage decreased (-0.1%) to 92.611% when pulling 1f15d5e on Cogitri:develop into c8e8989 on ColinDuquesnoy:develop.

@Cogitri
Copy link
Contributor Author

Cogitri commented Oct 29, 2017

Okay, it should be good to go now!

@Cogitri Cogitri changed the title [WIP]Plex plugin: initial bring up Plex plugin: initial bring up Oct 29, 2017
@ColinDuquesnoy
Copy link
Owner

Very good work 👍

Just one last thing: do you think you can adapt logo.svg to match the style of the other services logo (gray background / white foreground)?

@ColinDuquesnoy
Copy link
Owner

screenshot_20171029_153728

@ColinDuquesnoy ColinDuquesnoy added this to the 3.2.0 milestone Oct 29, 2017
@Cogitri
Copy link
Contributor Author

Cogitri commented Oct 29, 2017

Very good work 👍

Well, thanks for your help :)

Just one last thing: do you think you can adapt logo.svg to match the style of the other services logo (gray background / white foreground)?

Umm..sorry, but I barely know anything about that, would you mind doing that?

EDIT: Actually, after testing it a bit, it seems like the media art isn't updated on every song change, I'll look into it
Hm, thought at first that my latest commit fixed it, but for some reason the media art still doesn't change for some songs, not sure what that's about.

@Cogitri
Copy link
Contributor Author

Cogitri commented Oct 30, 2017

Okay, I still get [E] [2017-10-30 20-36-38:698877] [js] XMLHttpRequest cannot load data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7. Invalid response. Origin 'http://192.168.1.72:32400' is therefore not allowed access. for the first song which comes up, but after that it works fine, looking into it.

EDIT: Hm, for some reason it works sometimes, but other times I still get this error ( with the same song).

@Cogitri
Copy link
Contributor Author

Cogitri commented Nov 4, 2017

Okay, so if I run console.log(artUrl) from the dev console it does return the correct artUrl, but it's not displayed correctly :/
If I drop console.warn(JSON.stringify(updateInfo)); into the update function it says that artUrl is empty though, hm

if (songHash !== previousSongHash) {
artUrl = "";
convertArtUrlToBase64();
if (artUrl === "" )
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

convertArtUrlToBase64 is asynchronous and set the global artUrl variable later when the response to the XmlHttpRequest has been received and the content loaded by the FileReader. So resetting previousSongHash would cause many redundant request while the response of the first one has not been received.

@ColinDuquesnoy
Copy link
Owner

Can you try the below code and paste the full content of js.log?

//-----------------------------------------------------------------------------
//
// This file is part of MellowPlayer.
//
// MellowPlayer is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// MellowPlayer is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with MellowPlayer.  If not, see <http://www.gnu.org/licenses/>.
//
//-----------------------------------------------------------------------------
// Based in part on utils.js of Nuvolaruntime, which is:
//     Copyright 2014-2017 Jiří Janoušek <[email protected]> under the terms of BSD-2:
//     Redistribution and use in source and binary forms, with or without modification,
//     are permitted provided that the following conditions are met:
//     1. Redistributions of source code must retain the above copyright notice, this list of
//     conditions and the following disclaimer.
//     2. Redistributions in binary form must reproduce the above copyright notice, this list
//     of conditions and the following disclaimer in the documentation and/or other materials
//     provided with the distribution.
//     THIS SOFTWARE IS PROVIDED BY DMITRY VYUKOV "AS IS" AND ANY EXPRESS OR IMPLIED
//     WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
//     MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
//     SHALL DMITRY VYUKOV OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
//     INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
//     LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
//     OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
//     LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
//     OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
//     ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

var previousSongHash = "";
var artUrl = "";

console.warn("initialization")

function convertArtUrlToBase64() {
    try {
        var artBlobUrl = document.getElementsByClassName('AudioVideoPlayerControls-buttonGroupLeft-3kwFX')[
            0].children[0].children[0].children[0].children[0].children[0].children[0].children[0].children[0]
            .style["background-image"];
        artBlobUrl = artBlobUrl.replace('url("', "").replace('")', "");

        console.warn("art blob url: " + artBlobUrl);

        // Get the data URL of the blob
        var request = new XMLHttpRequest();
        request.open('GET', artBlobUrl, true);
        request.responseType = 'blob';
        request.onload = function() {
            console.warn("http response received");
            var reader = new FileReader();
            reader.onload = function(e) {
                console.warn("file reader on load");
                artUrl = e.target.result;
                console.warn("base 64 url: " + artUrl);
            };
            reader.readAsDataURL(request.response);
        };
        request.send();
        console.warn("http request sent");
    } catch (e) {
        console.warn("failed to convertArtUrl: ", e);
    }
}

function update() {
    var controlClassName = document.getElementsByClassName('AudioVideoPlayerControls-controls-OwK1f')[0];
    // if controlClassName is undefined the audioplayer isn't opened yet
    if (!controlClassName)
        return;
    var playbackStatus = mellowplayer.PlaybackStatus.STOPPED;
    if (document.querySelector('[aria-label=Pause]') !== null)
        playbackStatus = mellowplayer.PlaybackStatus.PLAYING;
    else if (document.querySelector('[aria-label=Play]') !== null)
        playbackStatus = mellowplayer.PlaybackStatus.PAUSED;

    // We'll use this multiple times later on as it packs all the media info
    var mediaInfoElement = document.getElementsByClassName('AudioVideoPlayerControls-buttonGroupLeft-3kwFX')[
        0].children[0].children[0];


    try {
        var songTitle = mediaInfoElement.children[1].children[0].title
    } catch (e) {
        var songTitle = '';
    }
    try {
        var artistInfoElement = mediaInfoElement.children[1].children[1];
        var artistName = artistInfoElement.children[0].title;
        var albumTitle = artistInfoElement.children[2].title;
    } catch (e) {
        var artistName = '';
        var albumTitle = '';
    }

    var mediaArtElement = mediaInfoElement.children[
        0].children[0].children[0].children[0].children[0].children[0]
    var songHash = getHashCode(songTitle + artistName);

    if (mediaArtElement && songHash !== previousSongHash) {
        console.warn("song changed: " + songTitle  + " by " + artistName);
        artUrl = "";
        convertArtUrlToBase64();
        previousSongHash = songHash;
    }

    var updateInfo = {
        "playbackStatus": playbackStatus,
        "canSeek": false,
        "canGoNext": true,
        "canGoPrevious": true,
        "canAddToFavorites": false,
        "volume": 1,
        "duration": mediaTime("duration"),
        "position": mediaTime("position"),
        "songId": songHash,
        "songTitle": songTitle,
        "artistName": artistName,
        "albumTitle": albumTitle,
        "artUrl": artUrl,
        "isFavorite": false
    };
    
    console.warn("update info: " + JSON.stringify(updateInfo));

    return updateInfo;
}

function mediaTime(type) {
    var timeElement = document.getElementsByClassName("DurationRemaining-container-1F4w8")[0]

    // if timeElement doesn't exist yet the site didn't finish loading yet
    if (!timeElement)
        return;
    else
        timeElement = timeElement.innerHTML;

    if (type === "position")
    // songPosition
        var time = timeElement.split("-->")[1].split("<!--")[0];
    else
    // songDuration
        var time = timeElement.split("-->")[5].split("<!--")[0];

    return toSeconds(time);
}

function clickButton(buttonName) {
    // Taken from: https://github.com/tiliado/nuvolaruntime/blob/master/src/mainjs/utils.js
    function triggerMouseEvent(elm, name, x, y) {
        var rect = elm.getBoundingClientRect();
        var width = rect.width * (x === undefined ? 0.5 : x);
        var height = rect.height * (y === undefined ? 0.5 : y);
        var opts = {
            view: document.defaultView,
            bubbles: true,
            cancelable: true,
            button: 0,
            relatedTarget: elm
        }
        opts.clientX = rect.left + width;
        opts.clientY = rect.top + height;
        opts.screenX = window.screenX + opts.clientX;
        opts.screenY = window.screenY + opts.clientY;
        var event = new MouseEvent(name, opts);
        elm.dispatchEvent(event);
    }

    function clickOnElement(elm, x, y) {
        triggerMouseEvent(elm, 'mouseover', x, y);
        triggerMouseEvent(elm, 'mousedown', x, y);
        triggerMouseEvent(elm, 'mouseup', x, y);
        triggerMouseEvent(elm, 'click', x, y);
    }

    clickOnElement(document.querySelector("[aria-label=" + buttonName + "]"))
}

function play() {
    clickButton('Play');
}

function pause() {
    clickButton('Pause');
}

function goNext() {
    clickButton('Next');
}

function goPrevious() {
    clickButton('Previous');
}

function setVolume(volume) {
    // not supported
}

function addToFavorites() {
    // not supported
}

function removeFromFavorites() {
    // not supported
}

function seekToPosition(position) {
    // not supported
}

@Cogitri
Copy link
Contributor Author

Cogitri commented Nov 4, 2017

@ColinDuquesnoy
Copy link
Owner

Can you remove js.log and try again (I can only see old log records)

@Cogitri
Copy link
Contributor Author

Cogitri commented Nov 4, 2017

Hm, I did..anyway, I just re-did it, here: https://paste.pound-python.org/show/2g2rhq19vDWxoYnduoQH/

@ColinDuquesnoy
Copy link
Owner

ColinDuquesnoy commented Nov 4, 2017

art blob url: data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7

It shows that sometimes the art url to download is not a blob url but a base64 image (1 white pixel). Nothing to do about that. Is that happening only with that song? Does that song sometimes have a valid blob url?

@Cogitri
Copy link
Contributor Author

Cogitri commented Nov 4, 2017

It always happens with the first song which pops up after the plex plugin loaded. Plex always provides a blob url/image for all songs, in case there's no cover set it plex itself provides a generic cover

@ColinDuquesnoy
Copy link
Owner

Ok. I don't see how to fix the first album art url being wrong, we'll have to live with that.

@ColinDuquesnoy ColinDuquesnoy merged commit d0e80d8 into ColinDuquesnoy:develop Nov 5, 2017
@Cogitri
Copy link
Contributor Author

Cogitri commented Nov 6, 2017

I guess so, thanks for reviewing!

@cpjeanpaul cpjeanpaul mentioned this pull request Jul 10, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants