diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 0ab2b0fc7..451774b99 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -1 +1,35 @@
-If you are interested in contributing to this project, please visit the [Development](https://github.com/muammar/mkchromecast/wiki/development) section in the Wiki.
+Contributing
+============
+
+If you want to contribute, help me improving this application by [reporting
+issues](https://github.com/muammar/mkchromecast/issues), [creating pull
+requests](https://github.com/muammar/mkchromecast/pulls) with your development/improvement.
+If your case is the latter, visit
+[Development](https://github.com/muammar/mkchromecast/wiki/development) section
+in the Wiki.
+
+Other way of contributing is donating on my Paypal so I can drink a beer or eat
+a Pizza :).
+[![paypal](https://www.paypalobjects.com/en_US/i/btn/btn_donateCC_LG.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=RZLF7TDCAXT9Q&lc=US&item_name=mkchromecast¤cy_code=USD&bn=PP%2dDonationsBF%3abtn_donateCC_LG%2egif%3aNonHosted)
+
+List of contributors
+--------------------
+
+| Author | loc | coms | fils | distribution |
+| ----------------------- | ------ | ---- | ---- | ------------ |
+| Muammar El Khatib | 354456 | 992 | 1248 | 100/98.8/99.0 |
+| Morten Linderud | 35 | 2 | 4 | 0.0/ 0.2/ 0.3 |
+| terencode | 22 | 2 | 1 | 0.0/ 0.2/ 0.1 |
+| Elias Tandel Barrionovo | 15 | 2 | 1 | 0.0/ 0.2/ 0.1 |
+| HairyFotr | 4 | 1 | 3 | 0.0/ 0.1/ 0.2 |
+| Maccer1 | 3 | 1 | 1 | 0.0/ 0.1/ 0.1 |
+| Matthew Aguirre | 1 | 1 | 1 | 0.0/ 0.1/ 0.1 |
+| Guillaume Roche | 1 | 1 | 1 | 0.0/ 0.1/ 0.1 |
+| Steindi01 | 0 | 1 | 0 | 0.0/ 0.1/ 0.0 |
+| Ioan Loosley | 0 | 1 | 0 | 0.0/ 0.1/ 0.0 |
+
+
+**Date**: Mon Jul 17 18:36:19 EDT 2017.
+
+A more up-to-date list can be see at
+[mkchromecast/graphs/contributors](https://github.com/muammar/mkchromecast/graphs/contributors).
diff --git a/Makefile b/Makefile
index b720b24d6..6cba9e65e 100644
--- a/Makefile
+++ b/Makefile
@@ -54,16 +54,16 @@ debug:
sed -i -e 's/tray = args.tray/tray = True/g' mkchromecast/__init__.py
sed -i -e 's/debug = args.debug/debug = True/g' mkchromecast/__init__.py
python3 setup.py py2app
- cp -R /usr/local/Cellar/qt/5.8.0_2/plugins dist/mkchromecast.app/Contents/PlugIns
- /usr/local/Cellar/qt/5.8.0_2/bin/macdeployqt dist/mkchromecast.app
+ cp -R /usr/local/Cellar/qt/5.9.1/plugins dist/Mkchromecast.app/Contents/PlugIns
+ /usr/local/Cellar/qt/5.9.1/bin/macdeployqt dist/Mkchromecast.app
# This target creates a standalone app with debugging disabled
deploy:
sed -i -e 's/tray = args.tray/tray = True/g' mkchromecast/__init__.py
sed -i -e 's/debug = args.debug/debug = False/g' mkchromecast/__init__.py
python3 setup.py py2app
- cp -R /usr/local/Cellar/qt/5.8.0_2/plugins dist/mkchromecast.app/Contents/PlugIns
- /usr/local/Cellar/qt/5.8.0_2/bin/macdeployqt dist/mkchromecast.app -dmg
+ cp -R /usr/local/Cellar/qt/5.9.1/plugins dist/Mkchromecast.app/Contents/PlugIns
+ /usr/local/Cellar/qt/5.9.1/bin/macdeployqt dist/Mkchromecast.app -dmg
# This cleans
clean:
diff --git a/README.md b/README.md
index 0eb87e947..5fac44f88 100644
--- a/README.md
+++ b/README.md
@@ -1,8 +1,8 @@
-mkchromecast
+Mkchromecast
============
[![License](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/muammar/mkchromecast/master/LICENSE)
[![PyPI](https://img.shields.io/pypi/pyversions/pychromecast.svg?maxAge=2592000)](https://github.com/muammar/mkchromecast/)
-[![node](https://img.shields.io/badge/node-7.10.0-yellow.svg)](https://github.com/muammar/mkchromecast/blob/master/nodejs/)
+[![node](https://img.shields.io/badge/node-9.3.0-yellow.svg)](https://github.com/muammar/mkchromecast/blob/master/nodejs/)
[![Downloads](https://img.shields.io/github/downloads/muammar/mkchromecast/total.svg?maxAge=2592000?style=flat-square)](https://github.com/muammar/mkchromecast/releases)
[![GitHub release](https://img.shields.io/github/release/muammar/mkchromecast.svg)](https://github.com/muammar/mkchromecast/releases/latest)
@@ -10,14 +10,14 @@ This is a program to cast your **macOS** audio, or **Linux** audio to your
Google Cast devices or Sonos speakers. It can also [cast video files](#video).
It is written in Python, and it can stream via `node.js`, `parec` (**Linux**),
-`ffmpeg`, or `avconv`. **mkchromecast** is capable of using lossy and lossless
+`ffmpeg`, or `avconv`. **Mkchromecast** is capable of using lossy and lossless
audio formats provided that `ffmpeg`, `avconv` (**Linux**), or `parec`
(**Linux**) are installed. It also supports [Multi-room group
playback](https://support.google.com/chromecast/answer/6329016?hl=en), and
[24-bit/96kHz high audio resolution](https://github.com/muammar/mkchromecast#high-quality-audio).
Additionally, a system tray menu is available.
-By default, **mkchromecast** streams with `node.js` (or `parec` in **Linux**)
+By default, **Mkchromecast** streams with `node.js` (or `parec` in **Linux**)
together with `mp3` audio coding format at a sample rate of `44100Hz` and
average bitrate of `192k`. These defaults can be changed using the
`--sample-rate` and `-b` flags. It is useful to modify these parameters when
@@ -37,10 +37,10 @@ backends.
Tell me more about it
----------------------
-To have an idea of using **mkchromecast** from console [check this
+To have an idea of using **Mkchromecast** from console [check this
gif](https://github.com/muammar/mkchromecast#usage).
-**mkchromecast** provides a **beta** system tray menu. It requires you to
+**Mkchromecast** provides a **beta** system tray menu. It requires you to
install `PyQt5`. For more information check the
[Requirements](https://github.com/muammar/mkchromecast#requirements) and
[Install](https://github.com/muammar/mkchromecast#install) sections.
@@ -66,7 +66,7 @@ Sonos support
--------------
If you have Sonos speakers, you can play whatever you are listening to in your
-computer with **mkchromecast**. To add Sonos support, install the `soco` python
+computer with **Mkchromecast**. To add Sonos support, install the `soco` python
module:
```
@@ -88,7 +88,7 @@ Requirements:
#### macOS
-In order to use **mkchromecast** you need the following software to stream with
+In order to use **Mkchromecast** you need the following software to stream with
`node.js`:
* Python2 (already shipped in OS X), or Python3.
@@ -103,7 +103,8 @@ following:
* flask (optional).
* ffmpeg (optional).
-* youtube-dl (option if you plan to cast youtube URLs).
+* youtube-dl (option if you plan to cast youtube URLs or [supported
+ websites](https://rg3.github.io/youtube-dl/supportedsites.html)).
#### Linux
@@ -122,8 +123,9 @@ following:
* ffmpeg (optional).
* avconv (optional).
* PyQt5 (optional if you want to use the system tray menu).
-* youtube-dl (optional if you plan to cast youtube URLs).
-* soco (this module adds Sonos support to mkchromecast).
+* youtube-dl (option if you plan to cast youtube URLs or [supported
+ websites](https://rg3.github.io/youtube-dl/supportedsites.html)).
+* soco (this module adds Sonos support to Mkchromecast).
For those who don't like Pulseaudio, it is possible to [cast using
ALSA](https://github.com/muammar/mkchromecast/wiki/ALSA). In that case the
@@ -145,8 +147,9 @@ requirements are:
* ffmpeg.
* avconv (optional).
* PyQt5 (optional if you want to use the system tray menu).
-* youtube-dl (optional if you plan to cast youtube URLs).
-* soco (this module adds Sonos support to mkchromecast).
+* youtube-dl (option if you plan to cast youtube URLs or [supported
+ websites](https://rg3.github.io/youtube-dl/supportedsites.html)).
+* soco (this module adds Sonos support to Mkchromecast).
Install
@@ -185,7 +188,7 @@ here](https://github.com/muammar/mkchromecast/issues).
* Debian (.deb): [https://packages.debian.org/mkchromecast](https://packages.debian.org/mkchromecast).
* Ubuntu (.deb): [http://packages.ubuntu.com/search?keywords=mkchromecast](http://packages.ubuntu.com/search?keywords=mkchromecast).
-**mkchromecast** is available in the [official Debian
+**Mkchromecast** is available in the [official Debian
repositories](https://packages.debian.org/mkchromecast). To install
it, just do:
@@ -225,7 +228,7 @@ apt-get install mkchromecast-pulseaudio (Pulseaudio users)
#### From sources
-To install **mkchromecast**, clone this repository:
+To install **Mkchromecast**, clone this repository:
```
git clone https://github.com/muammar/mkchromecast.git
@@ -236,7 +239,7 @@ here](https://github.com/muammar/mkchromecast/releases), and unzip the file.
##### Arch Linux
-mkchromecast is available at the AUR : [https://aur.archlinux.org/packages/mkchromecast-git/](https://aur.archlinux.org/packages/mkchromecast-git/).
+Mkchromecast is available at the AUR : [https://aur.archlinux.org/packages/mkchromecast-git/](https://aur.archlinux.org/packages/mkchromecast-git/).
```bash
#install with yaourt
@@ -319,15 +322,14 @@ Homebrew installed in your machine:
Once Homebrew is ready, you can install `ffmpeg`. As stated in the [ffmpeg
website](https://trac.ffmpeg.org/wiki/CompilationGuide/MacOSX), and for being
-able to use all audio coding formats in **mkchromecast**, it is better to
+able to use all audio coding formats in **Mkchromecast**, it is better to
install `ffmpeg` with the following options enabled:
```
-brew install ffmpeg --with-fdk-aac --with-tools --with-freetype --with-libass --with-libvorbis --with-libvpx --with-x265
-
+brew install ffmpeg --with-fdk-aac --with-tools --with-freetype --with-libass --with-libvorbis --with-libvpx --with-x265 --with-opus
```
-**mkchromecast** does not support `avconv` in **macOS**.
+**Mkchromecast** does not support `avconv` in **macOS**.
###### Linux
@@ -350,6 +352,7 @@ apt-get install libav-tools
`mp3` | MPEG Audio Layer III (default) | Lossy compression format (default bitrate: 192k)
`ogg` | Ogg Vorbis | Lossy compression format (default bitrate: 192k)
`aac` | Advanced Audio Coding (AAC) | Lossy compression format (default bitrate: 192k)
+ `opus` | Opus | Lossy compression format (default bitrate: 192k)
`wav` | Waveform Audio File Format | Lossless format (HQ sound)
`flac` | Free Lossless Audio Codec | Lossless format (HQ sound)
@@ -393,14 +396,14 @@ or if you desire it you can do it yourself from the sources.
Updating
--------
-To update **mkchromecast** sources, just get into the cloned directory and:
+To update **Mkchromecast** sources, just get into the cloned directory and:
```
git pull
```
or if you prefer it, you can just pass the `--update` argument to
-**mkchromecast**:
+**Mkchromecast**:
```
python mkchromecast.py --update
@@ -423,33 +426,33 @@ Usage
#### Audio
-Get into the cloned **mkchromecast** directory and execute:
+Get into the cloned **Mkchromecast** directory and execute:
```
python mkchromecast.py
```
-This will launch **mkchromecast** using `node.js` (or `parec` for **Linux**
+This will launch **Mkchromecast** using `node.js` (or `parec` for **Linux**
users), and will do the streaming part together with the `mp3` audio coding
format. `node.js` works decently but the server may tend to _fail_ under certain
-circumstances. In such a case, **mkchromecast** is able to restart the
+circumstances. In such a case, **Mkchromecast** is able to restart the
_streaming/casting_ process automatically. So, some hiccups are expected.
**Note**: most of the steps described herein are the same for **macOS** and **Linux**
users. However, if you launch the command above in **Linux**, the process is
less automatized. In **Linux**, you need to select with `pavucontrol` the sink
-called `mkchromecast` to stream unless you are using [ALSA](https://github.com/muammar/mkchromecast/wiki/ALSA).
+called `Mkchromecast` to stream unless you are using [ALSA](https://github.com/muammar/mkchromecast/wiki/ALSA).
See the [wiki for more information](https://github.com/muammar/mkchromecast/wiki/Linux). tl;dr?, just
check the gif below.
![Example of using mkchromecast](https://raw.githubusercontent.com/muammar/mkchromecast/master/images/mkchromecast_linux.gif)
**Note**: the cast process is independent from the selection of the pulseaudio
-sink. This means that **mkchromecast** will tell the cast device to listen your
+sink. This means that **Mkchromecast** will tell the cast device to listen your
computer but no sound will be heard until you select the sink. For ALSA users, this
does not apply.
-##### Using the `ffmpeg` backend with **mkchromecast** installed from sources
+##### Using the `ffmpeg` backend with **Mkchromecast** installed from sources
Below an example using `mp3`:
@@ -480,7 +483,7 @@ than one active network connection or when the automatically ip detection fails:
python mkchromecast.py --host 192.168.1.1
```
-##### Other examples with **mkchromecast** installed using the debian package
+##### Other examples with **Mkchromecast** installed using the debian package
To cast using `parec` and `wav` audio coding format:
```
@@ -502,7 +505,7 @@ mkchromecast --encoder-backend ffmpeg -c ogg -b 128 --sample-rate 48000
**Note**: to use `avconv` just replace from `ffmpeg` to `avconv` in the
commands above.
-##### Using **mkchromecast** from the system tray
+##### Using **Mkchromecast** from the system tray
To launch it:
@@ -521,7 +524,9 @@ here](https://github.com/muammar/mkchromecast#macos).
#### Playing Youtube URLs in Google Cast devices
-You can play Youtube URLs headlessly from the command line:
+You can play Youtube URLs (or [other
+sites](https://rg3.github.io/youtube-dl/supportedsites.html) headlessly from
+the command line:
```
python mkchromecast.py -y https://www.youtube.com/watch\?v\=NVvAJhZVBT
@@ -531,7 +536,8 @@ To use this function, you need to install `youtube-dl`. In macOS, this can be
done with homebrew: `brew install youtube-dl`. In Debian based distros:
`apt-get install youtube-dl`.
-**Note**: you may need to enclose the URL between quotation marks.
+**Note**: you may need to enclose the URL between quotation marks, and only
+URLs over `https` are supported.
#### Playing source URLs in Google Cast devices
@@ -560,7 +566,7 @@ As it can be seen above, **the codec has to be specified with the `-c` flag**.
#### Controlling the Google Cast's volume and pause/resume options
You can control the volume of your Google Cast device by launching
-**mkchromecast** with the option `--control`:
+**Mkchromecast** with the option `--control`:
```
python mkchromecast.py --encoder-backend ffmpeg -c ogg -b 320 --control
@@ -573,7 +579,7 @@ The system tray has a window with a volume slider to do `volume up` and `volume
#### High quality audio
-**mkchromecast** lets you cast using `24-bit/96kHz` high audio resolution. This
+**Mkchromecast** lets you cast using `24-bit/96kHz` high audio resolution. This
is the *maximum chromecast audio capability*. The supported codecs are: `wav`
and `flac`. In spite of the fact that `aac` can use `96000Hz` sample rate, the
bitrate corresponds to that of a lossy data compression format. Therefore, the
@@ -589,7 +595,7 @@ References:
#### Video
-You can now cast videos to your Google cast using **mkchromecast**. This feature works both with `node`
+You can now cast videos to your Google cast using **Mkchromecast**. This feature works both with `node`
and `ffmpeg` backends and from command line. In the future, they may be a graphical interface
for this process. [See this project](https://github.com/muammar/mkchromecast/projects/1).
@@ -633,7 +639,7 @@ python mkchromecast.py -y https://www.youtube.com/watch\?v\=VuMBaAZn3II --video
Killing the application
-----------------------
-To kill **mkchromecast** when you run it from console, just press
+To kill **Mkchromecast** when you run it from console, just press
Ctrl-C or q key to quit (when `--control` flag is passed).
When launching from system tray, use the `Quit` button in the system tray.
@@ -658,7 +664,7 @@ Known issues
------------
##### General
-* **mkchromecast**'s versions lower than 0.3.7 cannot operate with newer
+* **Mkchromecast**'s versions lower than 0.3.7 cannot operate with newer
versions of pychromecast.
* When casting videos using the `node` backend, it is not possible to
use neither the `--subtitle` nor the `--seek` flags.
@@ -668,7 +674,7 @@ Known issues
##### macOS
-* **mkchromecast** v0.3.6 cannot connect to selected chromecast when there are
+* **Mkchromecast** v0.3.6 cannot connect to selected chromecast when there are
more than one available. In that case, you need to use the application from
sources or build the application as shown
[here](https://github.com/muammar/mkchromecast/wiki/macOS-standalone-app).
diff --git a/archive/mkchromecast.png b/archive/mkchromecast.png
deleted file mode 100644
index 2a25e91cc..000000000
Binary files a/archive/mkchromecast.png and /dev/null differ
diff --git a/changelog.md b/changelog.md
index c3c31df7c..3c5c8e154 100644
--- a/changelog.md
+++ b/changelog.md
@@ -1,3 +1,28 @@
+* mkchromecast (0.3.8) **2017/12/23**
+
+ - Improved stability.
+ - The macOS bundle is now renamed with capital M, and will not be showing
+ in the dock.
+ - Chunk size changed from 1024 to 64. Added two more variables that
+ decreases the delay considerably.
+ - Improved ffmpeg commands for pulseaudio part of the code.
+ - node streaming updated to work with `node v9.3.0`.
+ - Manpage is now shipped in main branches.
+ - TypeError caused by a print statement for Soco devices has been fixed.
+ Closes #80.
+ - Added Opus codec support.
+ - It is now possible to screencast using mkchromecast.
+ - Using the `--youtube` flag works with all supported websites by youtube-dl.
+ - Correct signal handling using the `signal` module. PR #87 by @Foxboron.
+ - Renamed `--reconnect` to `--hijack`. Closes #25.
+ - New flag `tries` to to limit the number of tries to connect to
+ a chromecast. Closes #54.
+ - Allow custom server port with ffmpeg or avconv. Related to #122.
+ - Error with "width not divisible by 2 (853x480)". Closes issue #119.
+ - The `segment_time` flag has been fixed. Closes issue #71.
+ - New flag `command` for setting a custom ffmpeg command. Closes issue
+ #109.
+
* mkchromecast (0.3.7.1) **2017/05/21**
- macOS bundle built in Yosemite to add more compatibility.
@@ -276,4 +301,3 @@
- In this beta release, the program casts to the first google cast found in
the list. If the node streaming server fails, the program reconnects.
-
diff --git a/man/mkchromecast.1 b/man/mkchromecast.1
new file mode 100644
index 000000000..89734dd4b
--- /dev/null
+++ b/man/mkchromecast.1
@@ -0,0 +1,52 @@
+.\" Hey, EMACS: -*- nroff -*-
+.\" (C) Copyright 2016 Muammar El Khatib ,
+.\"
+.\" First parameter, NAME, should be all caps
+.\" Second parameter, SECTION, should be 1-8, maybe w/ subsection
+.\" other parameters are allowed: see man(7), man(1)
+.TH Mkchromecast "1" "March 30 2017"
+.\" Please adjust this date whenever revising the manpage.
+.\"
+.\" Some roff macros, for reference:
+.\" .nh disable hyphenation
+.\" .hy enable hyphenation
+.\" .ad l left justify
+.\" .ad b justify to both left and right margins
+.\" .nf disable filling
+.\" .fi enable filling
+.\" .br insert line break
+.\" .sp insert n+1 empty lines
+.\" for manpage-specific macros, see man(7)
+.SH NAME
+mkchromecast \- cast audio and video to Google cast devices
+.SH SYNOPSIS
+.B mkchromecast
+.RI [ options ]
+.SH DESCRIPTION
+This manual page documents briefly the
+.B mkchromecast
+commands.
+.PP
+.\" TeX users may be more comfortable with the \fB\fP and
+.\" \fI\fP escape sequences to invode bold face and italics,
+.\" respectively.
+\fBmkchromecast\fP is a program that allows you to cast your audio or video
+files to your Google cast devices.
+.SH OPTIONS
+This program follows the usual GNU command line syntax, with long
+options starting with two dashes (`-').
+A summary of options is included below.
+For a complete description, see the Info files.
+.TP
+.B \-h, \-\-help
+Show summary of options.
+.TP
+.B \-v, \-\-version
+Show version of program.
+.SH SEE ALSO
+.\".BR bar (1),
+.\".BR baz (1).
+.\".br
+The program is documented fully executing
+.IR "mkchromecast -h" ,
+that should give you complete access to the usage.
diff --git a/mkchromecast.desktop b/mkchromecast.desktop
new file mode 100644
index 000000000..155a2fa12
--- /dev/null
+++ b/mkchromecast.desktop
@@ -0,0 +1,9 @@
+[Desktop Entry]
+Name=mkchromecast
+Comment=Cast your Linux audio or video files to your Google cast or Sonos devices
+Exec=/usr/bin/mkchromecast -t
+Terminal=false
+Type=Application
+Icon=/usr/share/pixmaps/mkchromecast.xpm
+Categories=AudioVideo;Audio;
+Keywords=audio;chromecast;google;cast;pychromecast;
diff --git a/mkchromecast.py b/mkchromecast.py
index 415e30af6..d687dd11f 100644
--- a/mkchromecast.py
+++ b/mkchromecast.py
@@ -13,6 +13,8 @@
import os.path
import time
import atexit
+import signal
+
class mk(object):
"""Class to manage cast process"""
@@ -32,8 +34,8 @@ def __init__(self):
self.control = mkchromecast.__init__.control
self.cc = casting()
- checkmktmp()
- writePidFile()
+ mkchromecast.__init__.checkmktmp()
+ mkchromecast.__init__.writePidFile()
"""
Initializing backend array
@@ -46,22 +48,23 @@ def __init__(self):
]
self.check_connection()
- if self.tray == False and self.videoarg == False:
+ if self.tray is False and self.videoarg is False:
if self.platform == 'Linux':
self.audio_linux()
else:
self.audio_macOS()
- elif self.tray == False and self.videoarg == True:
+ elif self.tray is False and self.videoarg is True:
self.cast_video()
else:
self.start_tray()
def audio_linux(self):
"""This method manages all related to casting audio in Linux"""
- if self.youtubeurl == None and self.source_url == None:
- if adevice == None:
+ if self.youtubeurl is None and self.source_url is None:
+ if self.adevice is None:
print('Creating Pulseaudio Sink...')
- print(colors.warning('Open Pavucontrol and Select the Mkchromecast Sink.'))
+ print(colors.warning('Open Pavucontrol and Select the '
+ 'Mkchromecast Sink.'))
create_sink()
print(colors.important('Starting Local Streaming Server'))
print(colors.success('[Done]'))
@@ -71,14 +74,15 @@ def audio_linux(self):
self.cc.play_cast()
self.show_control(self.control)
- elif self.youtubeurl == None and self.source_url != None:
+ elif self.youtubeurl is None and self.source_url is not None:
self.start_backend(self.encoder_backend)
self.cc.initialize_cast()
self.get_cc(self.select_cc)
self.cc.play_cast()
self.show_control(self.control)
- elif youtubeurl != None and self.videoarg == False: # When casting youtube url, we do it through the audio module
+ # When casting youtube url, we do it through the audio module
+ elif youtubeurl is not None and self.videoarg is False:
import mkchromecast.audio
mkchromecast.audio.main()
self.cc.initialize_cast()
@@ -88,7 +92,7 @@ def audio_linux(self):
def audio_macOS(self):
"""This method manages all related to casting audio in macOS"""
- if self.youtubeurl == None and self.source_url == None:
+ if self.youtubeurl is None and self.source_url is None:
self.start_backend(self.encoder_backend)
self.cc.initialize_cast()
self.get_cc(self.select_cc)
@@ -100,7 +104,7 @@ def audio_macOS(self):
self.cc.play_cast()
self.show_control(self.control)
- elif self.youtubeurl == None and self.source_url != None:
+ elif self.youtubeurl is None and self.source_url is not None:
self.start_backend(self.encoder_backend)
self.cc.initialize_cast()
self.get_cc(self.select_cc)
@@ -114,7 +118,8 @@ def audio_macOS(self):
self.cc.play_cast()
self.show_control(self.control)
- elif self.youtubeurl != None and self.videoarg == False: # When casting youtube url, we do it through the audio module
+ # When casting youtube url, we do it through the audio module
+ elif self.youtubeurl is not None and self.videoarg is False:
import mkchromecast.audio
mkchromecast.audio.main()
self.cc.initialize_cast()
@@ -136,7 +141,8 @@ def get_cc(self, select_cc):
"""Get chromecast name, and let user select one from a list if
select_cc flag is True.
"""
- if select_cc == True: # This is done for the case that -s is passed
+ # This is done for the case that -s is passed
+ if select_cc is True:
self.cc.sel_cc()
self.cc.inp_cc()
self.cc.get_cc()
@@ -145,29 +151,30 @@ def get_cc(self, select_cc):
def start_backend(self, encoder_backend):
"""Starting backends"""
- if encoder_backend == 'node' and self.source_url == None:
+ if encoder_backend == 'node' and self.source_url is None:
from mkchromecast.node import stream
stream()
- elif encoder_backend in self.backends and self.source_url == None:
+ elif encoder_backend in self.backends and self.source_url is None:
import mkchromecast.audio
mkchromecast.audio.main()
def check_connection(self):
"""Check if the computer is connected to a network"""
if self.cc.ip == '127.0.0.1': # We verify the local IP.
- print(colors.error('Your Computer is not Connected to Any Network'))
+ print(colors.error('Your Computer is not Connected to Any '
+ 'Network'))
terminate()
- elif self.cc.ip != '127.0.0.1' and self.discover == True:
+ elif self.cc.ip != '127.0.0.1' and self.discover is True:
self.cc.initialize_cast()
terminate()
def terminate_app(self):
"""Terminate the app (kill app)"""
self.cc.stop_cast()
- if platform == 'Darwin':
+ if self.platform == 'Darwin':
inputint()
outputint()
- elif platform == 'Linux' and adevice == None:
+ elif self.platform == 'Linux' and self.adevice is None:
remove_sink()
terminate()
@@ -179,7 +186,7 @@ def controls_msg(self):
print('')
print(colors.options( 'Volume Up:')+' u')
print(colors.options( 'Volume Down:')+' d')
- if videoarg == True:
+ if self.videoarg is True:
print(colors.options( 'Pause Casting:')+' p')
print(colors.options( 'Resume Casting:')+' r')
print(colors.options('Quit the Application:')+' q or Ctrl-C')
@@ -187,7 +194,7 @@ def controls_msg(self):
def show_control(self, control):
"""Method to show controls"""
- if control == True:
+ if self.control is True:
from mkchromecast.getch import getch, pause
self.controls_msg()
@@ -197,30 +204,30 @@ def show_control(self, control):
if(key == 'u'):
self.cc.volume_up()
if self.encoder_backend == 'ffmpeg':
- if self.debug == True:
+ if self.debug is True:
self.controls_msg()
elif(key == 'd'):
self.cc.volume_down()
if self.encoder_backend == 'ffmpeg':
- if self.debug == True:
+ if self.debug is True:
self.controls_msg()
elif(key == 'p'):
- if self.videoarg == True:
+ if self.videoarg is True:
print('Pausing Casting Process...')
action = 'pause'
self.backend_handler(action, self.encoder_backend)
if self.encoder_backend == 'ffmpeg':
- if self.debug == True:
+ if self.debug is True:
self.controls_msg()
else:
pass
elif(key == 'r'):
- if self.videoarg == True:
+ if self.videoarg is True:
print('Resuming Casting Process...')
action = 'resume'
self.backend_handler(action, self.encoder_backend)
if self.encoder_backend == 'ffmpeg':
- if self.debug == True:
+ if self.debug is True:
self.controls_msg()
else:
pass
@@ -234,15 +241,17 @@ def show_control(self, control):
self.terminate_app()
else:
- if self.platform == 'Linux' and self.adevice == None:
- print(colors.warning('Remember to open pavucontrol and select the mkchromecast sink.'))
+ if self.platform == 'Linux' and self.adevice is None:
+ print(colors.warning('Remember to open pavucontrol and select '
+ 'the mkchromecast sink.'))
print('')
print(colors.error('Ctrl-C to kill the Application at any Time'))
print('')
- try:
- input()
- except KeyboardInterrupt:
- atexit.register(self.terminate_app())
+ signal.signal(signal.SIGINT,
+ lambda *_: atexit.register(self.terminate_app()))
+ signal.signal(signal.SIGTERM,
+ lambda *_: atexit.register(self.terminate_app()))
+ signal.pause()
def backend_handler(self, action, backend):
"""Methods to handle pause and resume state of backends"""
@@ -250,20 +259,24 @@ def backend_handler(self, action, backend):
subprocess.call(['pkill', '-STOP', '-f', 'ffmpeg'])
elif action == 'resume' and backend == 'ffmpeg':
subprocess.call(['pkill', '-CONT', '-f', 'ffmpeg'])
- elif action == 'pause' and backend == 'node' and self.platform == 'Linux':
+ elif (action == 'pause' and backend == 'node' and
+ self.platform == 'Linux'):
subprocess.call(['pkill', '-STOP', '-f', 'nodejs'])
- elif action == 'resume' and backend == 'node' and self.platform == 'Linux':
+ elif (action == 'resume' and backend == 'node' and
+ self.platform == 'Linux'):
subprocess.call(['pkill', '-CONT', '-f', 'nodejs'])
- elif action == 'pause' and backend == 'node' and self.platform == 'Darwin':
+ elif (action == 'pause' and backend == 'node' and
+ self.platform == 'Darwin'):
subprocess.call(['pkill', '-STOP', '-f', 'node'])
- elif action == 'resume' and backend == 'node' and self.platform == 'Darwin':
+ elif (action == 'resume' and backend == 'node' and
+ self.platform == 'Darwin'):
subprocess.call(['pkill', '-CONT', '-f', 'node'])
def start_tray(self):
"""This method starts the system tray"""
import mkchromecast.systray
- checkmktmp()
- writePidFile()
+ mkchromecast.__init__.checkmktmp()
+ mkchromecast.__init__.writePidFile()
mkchromecast.systray.main()
if __name__ == "__main__":
diff --git a/mkchromecast/__init__.py b/mkchromecast/__init__.py
index 30280a622..e6e2f86c1 100644
--- a/mkchromecast/__init__.py
+++ b/mkchromecast/__init__.py
@@ -1,10 +1,11 @@
#!/usr/bin/env python
-# This file is part of mkchromecast.
+# This file is part of Mkchromecast.
import mkchromecast.colors as colors
-from mkchromecast.utils import terminate
+from mkchromecast.utils import terminate, check_url
from mkchromecast.version import __version__
+from mkchromecast.resolution import resolutions
import argparse
import os.path
import sys
@@ -14,474 +15,535 @@
from argparse import RawTextHelpFormatter
parser = argparse.ArgumentParser(
-description='''
-This is a program to cast your macOS audio, or Linux audio to your Google Cast
-devices.
+ description='''
+ This is a program to cast your macOS audio, or Linux audio to your Google
+ Cast devices.
-It is written in Python, and it can stream via node.js, ffmpeg, parec (Linux
-pulseaudio users), avconv (Linux only) or ALSA (Linux users). mkchromecast is
-capable of using lossy and lossless audio formats provided that ffmpeg, avconv
-or parec is installed. Additionally, a system tray menu is available.
+ It is written in Python, and it can stream via node.js, ffmpeg, parec
+ (Linux pulseaudio users), avconv (Linux only) or ALSA (Linux users).
+ Mkchromecast is capable of using lossy and lossless audio formats provided
+ that ffmpeg, avconv or parec is installed. Additionally, a system tray
+ menu is available.
-Linux users that have installed the debian package need to launch the command
-`mkchromecast`, e.g.:
+ Linux users that have installed the debian package need to launch the
+ command `mkchromecast`, e.g.:
- mkchromecast
+ mkchromecast
-whereas, installation from source needs users to go inside the cloned git
-repository and execute:
+ whereas, installation from source needs users to go inside the cloned git
+ repository and execute:
- python mkchromecast.py
+ python mkchromecast.py
-The two examples above will make mkchromecast streams with node.js (or parec in
-Linux) together with mp3 audio coding format at a sample rate of 44100Hz and an
-average bitrate of 192k (defaults). These defaults can be changed using the
---sample-rate and -b flags. It is useful to modify these parameters when your
-wireless router is not very powerful, or in the case you don't want to degrade
-the sound quality. For more information visit the wiki and the FAQ
-https://github.com/muammar/mkchromecast/wiki/.
+ The two examples above will make Mkchromecast streams with node.js (or
+ parec in Linux) together with mp3 audio coding format at a sample rate of
+ 44100Hz and an average bitrate of 192k (defaults). These defaults can be
+ changed using the --sample-rate and -b flags. It is useful to modify these
+ parameters when your wireless router is not very powerful, or in the case
+ you don't want to degrade the sound quality. For more information visit the
+ wiki and the FAQ https://github.com/muammar/mkchromecast/wiki/.
-''',
-formatter_class=RawTextHelpFormatter
-)
-
-parser.add_argument(
-'--alsa-device',
-type=str,
-default=None,
-help=
-'''
-Set the ALSA device name. This option is useful when you are using pure
-ALSA in your system.
+ ''',
+ formatter_class=RawTextHelpFormatter
+ )
-Example:
- python mkchromecast.py --encoder-backend ffmpeg --alsa-device hw:2,1
+parser.add_argument(
+ '--alsa-device',
+ type=str,
+ default=None,
+ help='''
+ Set the ALSA device name. This option is useful when you are using pure
+ ALSA in your system.
+
+ Example:
+ python mkchromecast.py --encoder-backend ffmpeg --alsa-device hw:2,1
+
+ It only works for the ffmpeg and avconv backends, and it is not useful for
+ pulseaudio users. For more information read the README.Debian file shipped
+ in the Debian package or https://github.com/muammar/mkchromecast/wiki/ALSA.
+ '''
+ )
+
+parser.add_argument(
+ '-b',
+ '--bitrate',
+ type=int,
+ default='192',
+ help='''
+ Set the audio encoder's bitrate. The default is set to be 192k average
+ bitrate.
+
+ Example:
+
+ ffmpeg:
+ python mkchromecast.py --encoder-backend ffmpeg -c ogg -b 128
-It only works for the ffmpeg and avconv backends, and it is not useful for
-pulseaudio users. For more information read the README.Debian file shipped in
-the Debian package or https://github.com/muammar/mkchromecast/wiki/ALSA.
-'''
-)
+ node:
+ python mkchromecast.py -b 128
+
+ This option works with all backends. The example above sets the average
+ bitrate to 128k.
+ '''
+ )
parser.add_argument(
-'-b',
-'--bitrate',
-type=int,
-default='192',
-help=
-'''
-Set the audio encoder's bitrate. The default is set to be 192k average bitrate.
+ '--chunk-size',
+ type=int,
+ default='64',
+ help='''
+ Set the chunk size base for streaming in the Flask server. Default to 64.
+ This option only works when using the ffmpeg or avconv backends. This
+ number is the base to set both the buffer_size (defined by
+ 2 * chunk_size**2) in Flask server and the frame_size (defined by 32
+ * chunk_size).
+
+ Example:
+
+ ffmpeg:
+ python mkchromecast.py --encoder-backend ffmpeg -c ogg -b 128 --chunk-size 2048
+
+ avconv:
+ python mkchromecast.py --encoder-backend avconv -c ogg -b 128 --chunk-size 64
-Example:
-
-ffmpeg:
- python mkchromecast.py --encoder-backend ffmpeg -c ogg -b 128
-
-node:
- python mkchromecast.py -b 128
-
-This option works with all backends. The example above sets the average
-bitrate to 128k.
-'''
-)
-
-parser.add_argument(
-'--chunk-size',
-type=int,
-default='1024',
-help=
-'''
-Set the chunk size for streaming in the Flask server. Default to 1024. This
-option only works when using the ffmpeg or avconv backends.
-
-Example:
-
-ffmpeg:
- python mkchromecast.py --encoder-backend ffmpeg -c ogg -b 128 --chunk-size 2048
-
-avconv:
- python mkchromecast.py --encoder-backend avconv -c ogg -b 128 --chunk-size 512
-'''
-)
-
-parser.add_argument(
-'-c',
-'--codec',
-type=str,
-default='mp3',
-help=
-'''
-Set the audio codec.
-
-Example:
- python mkchromecast.py --encoder-backend ffmpeg -c ogg
-
-Possible codecs:
- - mp3 [192k] MPEG Audio Layer III (default)
- - ogg [192k] Ogg Vorbis
- - aac [192k] Advanced Audio Coding (AAC)
- - wav [HQ] Waveform Audio File Format
- - flac [HQ] Free Lossless Audio Codec
-
-This option only works for the ffmpeg, avconv and parec backends.
-'''
-)
-
-parser.add_argument(
-'--config',
-action='store_true',
-help='''
-Use this option to connect from configuration file.
-'''
-)
-
-parser.add_argument(
-'--control',
-action='store_true',
-default=False,
-help='''
-Control some actions of your Google Cast Devices. Use the 'u' and 'd' keys to
-perform volume up and volume down respectively, or press 'p' and 'r' to pause
-and resume cast process (only works with ffmpeg). Note that to kill the
-application using this option, you need to press the 'q' key or 'Ctrl-c'.
-'''
-)
-
-
-parser.add_argument(
-'--debug',
-action='store_true',
-help='''
-Option for debugging purposes.
-'''
-)
-
-parser.add_argument(
-'-d',
-'--discover',
-action='store_true',
-default=False,
-help='''
-Use this option if you want to know the friendly name of a Google Cast device.
-'''
-)
-
-parser.add_argument(
-'--encoder-backend',
-type=str,
-default=None,
-help=
-'''
-Set the backend for all encoders.
-Possible backends:
- - node (default in macOS)
- - parec (default in Linux)
- - ffmpeg
- - avconv
- - gstreamer
-
-Example:
- python mkchromecast.py --encoder-backend ffmpeg
-'''
-)
-
-parser.add_argument(
-'--host',
-type=str,
-default=None,
-help=
-'''
-Set the ip of the local host. This option is useful if the local ip of your
-computer is not being detected correctly, or in the case you have more than one
-network device available.
-
-Example:
- python mkchromecast.py --encoder-backend ffmpeg --host 192.168.1.1
-
-You can pass it to all available backends.
-'''
-)
-
-parser.add_argument(
-'-i',
-'--input-file',
-type=str,
-default=None,
-help='''
-Stream a file.
-
-Example:
- python mkchromecast.py -i /path/to/file.mp4
-'''
-)
-
-parser.add_argument(
-'-n',
-'--name',
-type=str,
-default=None,
-help='''
-Use this option if you know the name of the Google Cast you want to connect.
-
-Example:
- python mkchromecast.py -n mychromecast
-'''
-)
-
-parser.add_argument(
-'--notifications',
-action='store_true',
-help='''
-Use this flag to enable the notifications.
-'''
-)
-
-
-parser.add_argument(
-'-r',
-'--reset',
-action='store_true',
-help='''
-When the application fails, and you have no audio in your computer, use this
-option to reset the computer's audio.
-'''
-)
-
-parser.add_argument(
-'--reboot',
-action='store_true',
-help='''
-Reboot the Google Cast device.
-'''
-)
-
-parser.add_argument(
-'--reconnect',
-action='store_true',
-default=False,
-help='''
-This flag monitors if connection with google cast has been lost, and try to
-reconnect.
-'''
-)
-
-parser.add_argument(
-'--resolution',
-type=str,
-default=None,
-help='''
-Set the resolution of the streamed video. The following resolutions are supported:
-
- - 480p.
- - 720p (1280x720).
- - 1080p (1920x1080).
- - 2K.
- - UHD (3840x2160).
- - 4K (4096x2160).
-'''
-)
+ '''
+ )
parser.add_argument(
-'-s',
-'--select-cc',
-action='store_true',
-help='''
-If you have more than one Google Cast device use this option.
-'''
-)
-
+ '-c',
+ '--codec',
+ type=str,
+ default='mp3',
+ help='''
+ Set the audio codec.
+
+ Example:
+ python mkchromecast.py --encoder-backend ffmpeg -c ogg
+
+ Possible codecs:
+ - mp3 [192k] MPEG Audio Layer III (default)
+ - ogg [192k] Ogg Vorbis
+ - aac [192k] Advanced Audio Coding (AAC)
+ - wav [HQ] Waveform Audio File Format
+ - flac [HQ] Free Lossless Audio Codec
+
+ This option only works for the ffmpeg, avconv and parec backends.
+ '''
+ )
+
parser.add_argument(
-'--sample-rate',
-type=int,
-default='44100',
-help='''
-Set the sample rate. The default sample rate obtained from avfoundation audio
-device input in ffmpeg using soundflower for macOS is 44100Hz (in Linux can be
-44100Hz or 48000Hz). You can change this in the Audio MIDI Setup in the
-"Soundflower (2ch)" audio device. You need to change the "Format" in both
-input/output from 44100Hz to maximum 96000Hz. I think that more than 48000Hz
-is not necessary, but this is up to the users' preferences.
+ '--command',
+ type=str,
+ default=None,
+ help='''
+ Set a ffmpeg or avconv command for streaming video.
+
+ Example:
+ python3 mkchromecast.py --video --command 'ffmpeg -re -i \
+ /path/to/myvideo.mp4 -map_chapters -1 -vcodec libx264 -preset ultrafast \
+ -tune zerolatency -maxrate 10000k -bufsize 20000k -pix_fmt yuv420p -g \
+ 60 -f mp4 -max_muxing_queue_size 9999 -movflags \
+ frag_keyframe+empty_moov pipe:1'
+
+ Note that for the output you have to use pipe:1 to stream. This option only
+ works for the ffmpeg, avconv backends.
+ '''
+ )
-Note that re-sampling to higher sample rates is not a good idea. It was indeed
-an issue in the chromecast audio. See: https://goo.gl/yNVODZ.
-
-Example:
-
-ffmpeg:
- python mkchromecast.py --encoder-backend ffmpeg -c ogg -b 128 --sample-rate 32000
+parser.add_argument(
+ '--config',
+ action='store_true',
+ help='''
+ Use this option to connect from configuration file.
+ '''
+ )
-node:
- python mkchromecast.py -b 128 --sample-rate 32000
+parser.add_argument(
+ '--control',
+ action='store_true',
+ default=False,
+ help='''
+ Control some actions of your Google Cast Devices. Use the 'u' and 'd' keys
+ to perform volume up and volume down respectively, or press 'p' and 'r' to
+ pause and resume cast process (only works with ffmpeg). Note that to kill
+ the application using this option, you need to press the 'q' key or
+ 'Ctrl-c'.
+ '''
+ )
-This option works for both backends. The example above sets the sample rate to
-32000Hz, and the bitrate to 128k.
+parser.add_argument(
+ '--debug',
+ action='store_true',
+ help='''
+ Option for debugging purposes.
+ '''
+ )
-Which sample rate to use?
-
- - 192000Hz: maximum sampling rate supported in google cast audio without
- using High Dynamic Range. Only supported by aac, wav and flac codecs.
- - 96000Hz: maximum sampling rate supported in google cast audio using High
- Dynamic Range. Only supported by aac, wav and flac codecs.
- - 48000Hz: sampling rate of audio in DVDs.
- - 44100Hz: sampling rate of audio CDs giving a 20 kHz maximum frequency.
- - 32000Hz: sampling rate of audio quality a little below FM radio bandwidth.
- - 22050Hz: sampling rate of audio quality of AM radio.
+parser.add_argument(
+ '-d',
+ '--discover',
+ action='store_true',
+ default=False,
+ help='''
+ Use this option if you want to know the friendly name of a Google Cast
+ device.
+ '''
+ )
-For more information see: http://wiki.audacityteam.org/wiki/Sample_Rates.
-'''
-)
+parser.add_argument(
+ '--encoder-backend',
+ type=str,
+ default=None,
+ help='''
+ Set the backend for all encoders.
+ Possible backends:
+ - node (default in macOS)
+ - parec (default in Linux)
+ - ffmpeg
+ - avconv
+ - gstreamer
+
+ Example:
+ python mkchromecast.py --encoder-backend ffmpeg
+ '''
+ )
parser.add_argument(
-'--seek',
-type=str,
-default=None,
-help=
-'''
-Option to seeking when casting video. The format to set the time is HH:MM:SS.
+ '--host',
+ type=str,
+ default=None,
+ help='''
+ Set the ip of the local host. This option is useful if the local ip of your
+ computer is not being detected correctly, or in the case you have more than
+ one network device available.
-Example:
- python mkchromecast.py --video -i "/path/to/file.mp4" --seek 00:23:00
+ Example:
+ python mkchromecast.py --encoder-backend ffmpeg --host 192.168.1.1
-'''
-)
+ You can pass it to all available backends.
+ '''
+ )
parser.add_argument(
-'--segment-time',
-type=int,
-default=None,
-help=
-'''
-Segmentate audio for improved live streaming when using ffmpeg.
+ '-i',
+ '--input-file',
+ type=str,
+ default=None,
+ help='''
+ Stream a file.
+
+ Example:
+ python mkchromecast.py -i /path/to/file.mp4
+ '''
+ )
-Example:
- python mkchromecast.py --encoder-backend ffmpeg --segment-time 2
+parser.add_argument(
+ '-n',
+ '--name',
+ type=str,
+ default=None,
+ help='''
+ Use this option if you know the name of the Google Cast you want to
+ connect.
+
+ Example:
+ python mkchromecast.py -n mychromecast
+ '''
+ )
-'''
-)
+parser.add_argument(
+ '--notifications',
+ action='store_true',
+ help='''
+ Use this flag to enable the notifications.
+ '''
+ )
parser.add_argument(
-'--source-url',
-type=str,
-default=None,
-help=
-'''
-This option allows you to pass any source URL to your Google Cast device. You
-have to specify the codec with -c flag when using it.
+ '-p',
+ '--port',
+ type=int,
+ default='5000',
+ help='''
+ Set the listening port for local webserver.
-Example:
+ Example:
-Source URL, port and extension:
- python mkchromecast.py --source-url http://192.99.131.205:8000/pvfm1.ogg -c ogg --control
+ ffmpeg:
+ python mkchromecast.py --encoder-backend ffmpeg -p 5100
-Source URL, no port, and extension:
- python mkchromecast.py --source-url http://example.com/name.ogg -c ogg --control
+ '''
+ )
-Source URL without extension:
- python mkchromecast.py --source-url http://example.com/name -c aac --control
+parser.add_argument(
+ '-r',
+ '--reset',
+ action='store_true',
+ help='''
+ When the application fails, and you have no audio in your computer, use
+ this option to reset the computer's audio.
+ '''
+ )
-Supported source URLs are:
+parser.add_argument(
+ '--reboot',
+ action='store_true',
+ help='''
+ Reboot the Google Cast device.
+ '''
+ )
- - http://url:port/name.mp3
- - http://url:port/name.ogg
- - http://url:port/name.mp4 (use the aac codec)
- - http://url:port/name.wav
- - http://url:port/name.flac
+parser.add_argument(
+ '--hijack',
+ action='store_true',
+ default=False,
+ help='''
+ This flag monitors if connection with google cast has been lost, and try to
+ hijack it.
+ '''
+ )
+
+parser.add_argument(
+ '--resolution',
+ type=str,
+ default=None,
+ help='''
+ Set the resolution of the streamed video. The following resolutions are
+ supported:
-.m3u or .pls are not yet available.
-'''
-)
+ ''' +
+ "\n".join(" - {0} ({2}).".format(k, *v) for k, v in resolutions.items())
+ )
parser.add_argument(
-'--subtitles',
-type=str,
-default=None,
-help=
-'''
-Set subtitles.
-'''
-)
+ '-s',
+ '--select-cc',
+ action='store_true',
+ help='''
+ If you have more than one Google Cast device use this option.
+ '''
+ )
parser.add_argument(
-'-t',
-'--tray',
-action='store_true',
-help='''
-This option let you launch mkchromecast as a systray menu (beta).
-'''
-)
+ '--sample-rate',
+ type=int,
+ default='44100',
+ help='''
+ Set the sample rate. The default sample rate obtained from avfoundation
+ audio device input in ffmpeg using soundflower for macOS is 44100Hz (in
+ Linux can be 44100Hz or 48000Hz). You can change this in the Audio MIDI
+ Setup in the "Soundflower (2ch)" audio device. You need to change the
+ "Format" in both input/output from 44100Hz to maximum 96000Hz. I think
+ that more than 48000Hz is not necessary, but this is up to the users'
+ preferences.
+
+ Note that re-sampling to higher sample rates is not a good idea. It was
+ indeed an issue in the chromecast audio. See: https://goo.gl/yNVODZ.
+
+ Example:
+
+ ffmpeg:
+ python mkchromecast.py --encoder-backend ffmpeg -c ogg -b 128 --sample-rate 32000
+
+ node:
+ python mkchromecast.py -b 128 --sample-rate 32000
+
+ This option works for both backends. The example above sets the sample rate
+ to 32000Hz, and the bitrate to 128k.
+
+ Which sample rate to use?
+
+ - 192000Hz: maximum sampling rate supported in google cast audio
+ without using High Dynamic Range. Only supported by aac, wav and flac
+ codecs.
+ - 96000Hz: maximum sampling rate supported in google cast audio using
+ High Dynamic Range. Only supported by aac, wav and flac codecs.
+ - 48000Hz: sampling rate of audio in DVDs.
+ - 44100Hz: sampling rate of audio CDs giving a 20 kHz maximum
+ frequency.
+ - 32000Hz: sampling rate of audio quality a little below FM radio
+ bandwidth.
+ - 22050Hz: sampling rate of audio quality of AM radio.
+
+ For more information see: http://wiki.audacityteam.org/wiki/Sample_Rates.
+ '''
+ )
parser.add_argument(
-'--update',
-action='store_true',
-help="""
-Update mkchromecast git repository.
+ '--screencast',
+ action='store_true',
+ default=False,
+ help='''
+ Use this flag to cast your Desktop Google cast devices. It is only working
+ with ffmpeg. You may want to you use the --resolution option together with
+ this flag.
-Example:
- python mkchromecast.py --update
+ Examples:
-This will execute for you:
+ python mkchromecast.py --video --screencast
+ '''
+ )
- git pull --all
- git fetch -p
-"""
-)
+parser.add_argument(
+ '--seek',
+ type=str,
+ default=None,
+ help='''
+ Option to seeking when casting video. The format to set the time is
+ HH:MM:SS.
+
+ Example:
+ python mkchromecast.py --video -i "/path/to/file.mp4" --seek 00:23:00
+
+ '''
+ )
+
+parser.add_argument(
+ '--segment-time',
+ type=int,
+ default=None,
+ help='''
+ Segmentate audio for improved live streaming when using ffmpeg.
+
+ Example:
+ python mkchromecast.py --encoder-backend ffmpeg --segment-time 2
+
+ '''
+ )
+
+parser.add_argument(
+ '--source-url',
+ type=str,
+ default=None,
+ help='''
+ This option allows you to pass any source URL to your Google Cast device.
+ You have to specify the codec with -c flag when using it.
+
+ Example:
+
+ Source URL, port and extension:
+ python mkchromecast.py --source-url http://192.99.131.205:8000/pvfm1.ogg -c ogg --control
+
+ Source URL, no port, and extension:
+ python mkchromecast.py --source-url http://example.com/name.ogg -c ogg --control
+
+ Source URL without extension:
+ python mkchromecast.py --source-url http://example.com/name -c aac --control
+
+ Supported source URLs are:
+
+ - http://url:port/name.mp3
+ - http://url:port/name.ogg
+ - http://url:port/name.mp4 (use the aac codec)
+ - http://url:port/name.wav
+ - http://url:port/name.flac
+
+ .m3u or .pls are not yet available.
+ '''
+ )
parser.add_argument(
-'-v',
-'--version',
-action='store_true',
-help='''
-Show the version'''
-)
+ '--subtitles',
+ type=str,
+ default=None,
+ help='''
+ Set subtitles.
+ '''
+ )
parser.add_argument(
-'--video',
-action='store_true',
-default=False,
-help='''
-Use this flag to cast video to your Google cast devices. It is only working
-with ffmpeg.
+ '-t',
+ '--tray',
+ action='store_true',
+ help='''
+ This option let you launch Mkchromecast as a systray menu (beta).
+ '''
+ )
-Examples:
+parser.add_argument(
+ '--tries',
+ type=int,
+ default=None,
+ help='''
+ Limit the number of times the underlying socket associated with your
+ Chromecast objects will retry connecting
+ '''
+ )
-Cast a file:
- python mkchromecast.py --video -i "/path/to/file.mp4"
+parser.add_argument(
+ '--update',
+ action='store_true',
+ help="""
+ Update Mkchromecast git repository.
-Cast from source-url:
- python mkchromecast.py --source-url http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4 -c mp4 --control --video
+ Example:
+ python mkchromecast.py --update
-Cast a youtube-url:
- python mkchromecast.py -y https://www.youtube.com/watch\?v\=VuMBaAZn3II --video
+ This will execute for you:
-'''
-)
+ git pull --all
+ git fetch -p
+ """
+ )
parser.add_argument(
-'--volume',
-action='store_true',
-default=False,
-help='''
-This option has been changed to --control. It will be deleted in following
-releases.
-'''
-)
+ '-v',
+ '--version',
+ action='store_true',
+ help='''
+ Show the version'''
+ )
parser.add_argument(
-'-y',
-'--youtube',
-type=str,
-default=None,
-help='''
-Stream from Youtube URL. This option needs youtube-dl.
+ '--video',
+ action='store_true',
+ default=False,
+ help='''
+ Use this flag to cast video to your Google cast devices. It is only working
+ with ffmpeg.
+
+ Examples:
+
+ Cast a file:
+ python mkchromecast.py --video -i "/path/to/file.mp4"
-Example:
- python mkchromecast.py -y https://www.youtube.com/watch?v=NVvAJhZVBTc
+ Cast from source-url:
+ python mkchromecast.py --source-url http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4 -c mp4 --control --video
-As I don't own a Google Cast for TVs, I cannot test this correctly. But in
-principle it should work.
-'''
-)
+ Cast a youtube-url:
+ python mkchromecast.py -y https://www.youtube.com/watch\?v\=VuMBaAZn3II --video
+
+ '''
+ )
+parser.add_argument(
+ '--volume',
+ action='store_true',
+ default=False,
+ help='''
+ This option has been changed to --control. It will be deleted in following
+ releases.
+ '''
+ )
+
+parser.add_argument(
+ '-y',
+ '--youtube',
+ type=str,
+ default=None,
+ help='''
+ Stream from sources supported by youtube-dl. This option needs
+ the youtube-dl package, and it also gives you access to all its
+ supported websites such as Dailymotion, LiveLeak, and Vimeo.
+
+ For a comprehensive list, check:
+ http://rg3.github.io/youtube-dl/supportedsites.html.
+
+ Example:
+ python mkchromecast.py -y https://www.youtube.com/watch?v=NVvAJhZVBTc
+
+ Note that this is only working for websites running over https.
+ '''
+ )
args = parser.parse_args()
@@ -494,36 +556,38 @@
Assignment of args to variables
"""
tray = args.tray
-if tray == True:
+if tray is True:
select_cc = True
else:
select_cc = args.select_cc
debug = args.debug
-if args.notifications == True:
+if args.notifications is True:
notifications = 'enabled'
else:
notifications = 'disabled'
adevice = args.alsa_device
-if debug == True:
- print('ALSA device name:', adevice)
+if debug is True:
+ print('ALSA device name: %s.' % adevice)
discover = args.discover
host = args.host
input_file = args.input_file
sourceurl = args.source_url
subtitles = args.subtitles
-reconnect = args.reconnect
-
+hijack = args.hijack
ccname = args.name
-if debug == True:
- print('Google Cast name:', ccname)
+port = args.port
+
+
+if debug is True:
+ print('Google Cast name: %s.' % ccname)
"""
Reset
"""
-if args.reset == True:
+if args.reset is True:
if platform == 'Darwin':
from mkchromecast.audio_devices import inputint, outputint
inputint()
@@ -536,14 +600,14 @@
"""
Reboot
"""
-if args.reboot == True:
+if args.reboot is True:
print(colors.error('This option is not implemented yet.'))
sys.exit(0)
"""
Not yet implemented
"""
-if args.config == True:
+if args.config is True:
print(colors.error('This option is not implemented yet.'))
sys.exit(0)
@@ -558,7 +622,7 @@
Update
"""
if args.update is True:
- print(colors.warning('Updating mkchromecast'))
+ print(colors.warning('Updating Mkchromecast'))
print(colors.important('git pull --all'))
pull = subprocess.Popen(
['git', 'pull', '--all'],
@@ -579,6 +643,8 @@
"""
Check that encoders exist in the list
"""
+screencast = args.screencast
+
backends = [
'node',
'ffmpeg',
@@ -586,35 +652,35 @@
]
if platform == 'Darwin':
backends.remove('avconv')
-elif platform == 'Linux' and args.video == True:
+elif platform == 'Linux' and args.video is True:
pass
else:
backends.remove('node')
backends.append('parec')
backends.append('gstreamer')
-if args.debug == True:
+if args.debug is True:
print('backends: ', backends)
-if args.encoder_backend not in backends and args.encoder_backend != None:
+if args.encoder_backend not in backends and args.encoder_backend is not None:
print(colors.error('Supported backends are: '))
for backend in backends:
- print('-',backend)
+ print('- %s.' % backend)
sys.exit(0)
if args.encoder_backend in backends:
backend = args.encoder_backend
-elif args.encoder_backend == None: #This is to define defaults
- if platform == 'Linux' and args.video == False:
+elif args.encoder_backend is None: # This is to define defaults
+ if platform == 'Linux' and args.video is False:
args.encoder_backend = 'parec'
backend = args.encoder_backend
- if platform == 'Linux' and args.video == True:
+ if platform == 'Linux' and args.video is True:
args.encoder_backend = 'ffmpeg'
backend = args.encoder_backend
- elif platform == 'Darwin' and args.video == True:
+ elif platform == 'Darwin' and args.video is True:
args.encoder_backend = 'ffmpeg'
backend = args.encoder_backend
- elif platform == 'Darwin' and args.video == False:
+ elif platform == 'Darwin' and args.video is False:
args.encoder_backend = 'node'
backend = args.encoder_backend
@@ -625,52 +691,60 @@
'mp3',
'ogg',
'aac',
+ 'opus',
'wav',
'flac'
]
-if backend == 'node' and args.codec != 'mp3' and sourceurl == None:
+if backend == 'node' and args.codec != 'mp3' and sourceurl is None:
rcodec = args.codec
codec = 'mp3'
-elif backend == 'node' and args.codec == 'mp3' and sourceurl == None:
+elif backend == 'node' and args.codec == 'mp3' and sourceurl is None:
rcodec = args.codec
codec = 'mp3'
-elif sourceurl != None:
+elif sourceurl is not None:
codec = args.codec
else:
rcodec = None
if backend != 'node' and args.codec in codecs:
codec = args.codec
else:
- print(colors.options('Selected audio codec: ')+ args.codec)
+ print(colors.options('Selected audio codec: %s.') % args.codec)
print(colors.error('Supported audio codecs are: '))
for codec in codecs:
- print('-', codec)
+ print('- %s.' % codec)
sys.exit(0)
+
+"""
+Command
+"""
+if args.command is not None and args.video is True:
+ safe_commands = ['ffmpeg', 'avconv']
+ command = args.command.split(' ')
+ if command[0] not in safe_commands:
+ print(colors.error('Refusing to execute this.'))
+ sys.exit(0)
+elif args.command is None and args.video is True:
+ command = args.command
+elif args.command is not None and args.video is False:
+ print(colors.warning('The --command option only works for video.'))
+
"""
Resolution
"""
-resolutions = [
- '480p',
- '720p',
- '1080p',
- '2K',
- 'UHD',
- '4K'
- ]
-_resolutions = [r.lower() for r in resolutions]
+resolutions = [r.lower() for r in resolutions.keys()]
-if args.resolution == None:
+if args.resolution is None:
resolution = args.resolution
-elif args.resolution.lower() in _resolutions:
+elif args.resolution.lower() in resolutions:
resolution = args.resolution.lower()
else:
print(colors.error('Supported resolutions are: '))
for res in resolutions:
- if res != False:
- print('-', res)
+ if res is not False:
+ print('- %s.' % res)
sys.exit(0)
"""
@@ -680,6 +754,7 @@
'mp3',
'ogg',
'aac',
+ 'opus',
'flac'
]
@@ -691,17 +766,15 @@
else:
bitrate = args.bit_rate
else:
- bitrate = None #When the codec does not require bitrate I set it to None
+ # When the codec does not require bitrate I set it to None
+ bitrate = None
"""
Chunk size
"""
if args.chunk_size <= 0:
- print(colors.warning('Chunk size set to default: 1024.'))
- chunk_size = 1024
-elif args.chunk_size < 512:
- print(colors.warning('Chunk size not recommended. Using 512 instead.'))
- chunk_size = 512
+ chunk_size = 64
+ print(colors.warning('Chunk size set to default: %s.' % chunk_size))
else:
chunk_size = abs(args.chunk_size)
@@ -712,6 +785,8 @@
if args.sample_rate < 22050:
print(colors.error('The sample rate has to be greater than 22049.'))
sys.exit(0)
+ elif args.codec == 'opus':
+ samplerate = 48000
else:
samplerate = abs(args.sample_rate)
elif args.sample_rate == 0:
@@ -726,14 +801,19 @@
Segment time
"""
avoid = ['parec', 'node']
+
if isinstance(args.segment_time, int) and backend not in avoid:
- segmenttime = args.segment_time
+ segment_time = args.segment_time
elif isinstance(args.segment_time, float) or backend in avoid:
- segmenttime = None
+ segment_time = None
else:
- print(colors.warning('The segment time has to be an integer number'))
- print(colors.warning('Set to default of 2 seconds'))
- segmenttime = 2
+ segment_time = None
+
+"""
+Tries
+"""
+
+tries = args.tries
"""
Video
@@ -744,16 +824,27 @@
Volume
"""
control = args.control
-if args.volume == True: #FIXME this has to be deleted in future releases.
+if args.volume is True: # FIXME this has to be deleted in future releases.
control = args.volume
- print(colors.warning('The --volume flag is going to be renamed to --control.'))
+ print(colors.warning('The --volume flag is going to be renamed to \
+ --control.'))
"""
Youtube URLs
"""
-if args.youtube != None:
- if 'https' not in args.youtube:
- print(colors.error('You need to provide a youtube URL'))
+if args.youtube is not None:
+ if check_url(args.youtube) is False:
+ youtube_error = """
+ You need to provide a URL that is supported by youtube-dl.
+ """
+ message = """
+ For a list of supported sources please visit:
+ https://rg3.github.io/youtube-dl/supportedsites.html
+
+ Note that the URLs have to start with https.
+ """
+ print(colors.error(youtube_error))
+ print(message)
sys.exit(0)
else:
youtubeurl = args.youtube
@@ -761,19 +852,25 @@
else:
youtubeurl = args.youtube
+
"""
This is to write a PID file
"""
+
+
def writePidFile():
- if os.path.exists('/tmp/mkchromecast.pid') == True: #This is to verify that pickle tmp file exists
- os.remove('/tmp/mkchromecast.pid')
+ # This is to verify that pickle tmp file exists
+ if os.path.exists('/tmp/mkchromecast.pid') is True:
+ os.remove('/tmp/mkchromecast.pid')
pid = str(os.getpid())
f = open('/tmp/mkchromecast.pid', 'wb')
pickle.dump(pid, f)
f.close()
return
+
def checkmktmp():
- if os.path.exists('/tmp/mkchromecast.tmp') == True: #This is to verify that pickle tmp file exists
- os.remove('/tmp/mkchromecast.tmp')
+ # This is to verify that pickle tmp file exists
+ if os.path.exists('/tmp/mkchromecast.tmp') is True:
+ os.remove('/tmp/mkchromecast.tmp')
return
diff --git a/mkchromecast/audio.py b/mkchromecast/audio.py
index a1c84e5d3..c571e0c81 100644
--- a/mkchromecast/audio.py
+++ b/mkchromecast/audio.py
@@ -7,9 +7,9 @@
"""
import mkchromecast.__init__
-from mkchromecast.audio_devices import *
+from mkchromecast.audio_devices import inputint, outputint
+from mkchromecast.config import config_manager
import mkchromecast.colors as colors
-from mkchromecast.config import *
import mkchromecast.messages as msg
from mkchromecast.preferences import ConfigSectionMap
import psutil
@@ -18,7 +18,7 @@
import time
from functools import partial
from subprocess import Popen, PIPE
-from flask import Flask, Response, request
+from flask import Flask, Response
import multiprocessing
import threading
import os
@@ -30,7 +30,7 @@
try:
import ConfigParser
except ImportError:
- import configparser as ConfigParser # This is for Python3
+ import configparser as ConfigParser # This is for Python3
backends_dict = {}
@@ -41,11 +41,17 @@
tray = mkchromecast.__init__.tray
adevice = mkchromecast.__init__.adevice
chunk_size = mkchromecast.__init__.chunk_size
-segmenttime = mkchromecast.__init__.segmenttime
+segment_time = mkchromecast.__init__.segment_time
+port = mkchromecast.__init__.port
+
+frame_size = 32 * chunk_size
+buffer_size = 2 * chunk_size**2
-if debug == True:
- print(':::audio::: chunk_size: ', chunk_size)
debug = mkchromecast.__init__.debug
+
+if debug is True:
+ print(':::audio::: chunk_size, frame_size, buffer_size: %s, %s, %s'
+ % (chunk_size, frame_size, buffer_size))
sourceurl = mkchromecast.__init__.sourceurl
config = ConfigParser.RawConfigParser()
configurations = config_manager() # Class from mkchromecast.config
@@ -58,7 +64,7 @@
youtubeurl = None
# This is to take the youtube URL
-if youtubeurl != None:
+if youtubeurl is not None:
print(colors.options('The Youtube URL chosen:')+' '+youtubeurl)
try:
@@ -70,7 +76,7 @@
url_data = urllib.parse.urlparse(youtubeurl)
query = urllib.parse.parse_qs(url_data.query)
video = query['v'][0]
- print(colors.options('Playing video:')+' '+video)
+ print(colors.options('Playing video:') + ' ' + video)
command = [
'youtube-dl',
'-o',
@@ -79,19 +85,19 @@
]
mtype = 'audio/mp4'
else:
- if os.path.exists(configf) and tray == True:
+ if os.path.exists(configf) and tray is True:
configurations.chk_config()
config.read(configf)
backend = ConfigSectionMap('settings')['backend']
backends_dict[backend] = backend
- codec= ConfigSectionMap('settings')['codec']
+ codec = ConfigSectionMap('settings')['codec']
bitrate = ConfigSectionMap('settings')['bitrate']
- samplerate= ConfigSectionMap('settings')['samplerate']
+ samplerate = ConfigSectionMap('settings')['samplerate']
adevice = ConfigSectionMap('settings')['alsadevice']
if adevice == 'None':
adevice = None
- if debug == True:
- print(':::audio::: tray ='+str(tray))
+ if debug is True:
+ print(':::audio::: tray = ' + str(tray))
print(colors.warning('Configuration file exists'))
print(colors.warning('Using defaults set there'))
print(backend, codec, bitrate, samplerate, adevice)
@@ -107,41 +113,42 @@
'avconv',
'parec'
]
- if tray == True and backend in backends:
- import os, getpass
- import subprocess
+ if tray is True and backend in backends:
+ import os
+ import getpass
USER = getpass.getuser()
- PATH = './bin:./nodejs/bin:/Users/' \
- +str(USER) \
- +'/bin:/usr/local/bin:/usr/local/sbin:/usr/bin:/bin:/usr/sbin:/sbin:/opt/X11/bin:/usr/X11/bin:/usr/games:' \
- +os.environ['PATH']
+ PATH = './bin:./nodejs/bin:/Users/' + \
+ str(USER) + \
+ '/bin:/usr/local/bin:/usr/local/sbin:/usr/bin:/bin:/usr/sbin:/sbin:/opt/X11/bin:/usr/X11/bin:/usr/games:' + os.environ['PATH']
iterate = PATH.split(':')
for item in iterate:
- verifyif = str(item+'/'+backend)
- if os.path.exists(verifyif) == False:
+ verifyif = str(item + '/' + backend)
+ if os.path.exists(verifyif) is False:
continue
else:
backends_dict[verifyif] = backend
backend = verifyif
- if debug == True:
- print(':::audio::: Program '+str(backend)+' found in '+str(verifyif))
- print(':::audio::: backend dictionary '+str(backends_dict))
+ if debug is True:
+ print(':::audio::: Program ' + str(backend) +
+ ' found in ' + str(verifyif))
+ print(':::audio::: backend dictionary ' +
+ str(backends_dict))
if codec == 'mp3':
- appendmtype = 'mpeg'
+ append_mtype = 'mpeg'
else:
- appendmtype = codec
+ append_mtype = codec
- mtype = 'audio/'+appendmtype
+ mtype = 'audio/' + append_mtype
- if sourceurl == None:
- print(colors.options('Selected backend:')+' '+ backend)
- print(colors.options('Selected audio codec:')+' '+ codec)
+ if sourceurl is None:
+ print(colors.options('Selected backend:') + ' ' + backend)
+ print(colors.options('Selected audio codec:') + ' ' + codec)
if backend != 'node':
if bitrate == '192':
- bitrate = bitrate+'k'
+ bitrate = bitrate + 'k'
msg.bitrate_default(bitrate)
elif bitrate == 'None':
msg.no_bitrate(codec)
@@ -156,10 +163,10 @@
bitrate = '500'
msg.maxbitrate(codec, bitrate)
else:
- bitrate = bitrate+'k'
+ bitrate = bitrate + 'k'
- if sourceurl == None:
- print(colors.options('Selected bitrate:')+' '+ bitrate)
+ if sourceurl is None:
+ print(colors.options('Selected bitrate:') + ' ' + bitrate)
if samplerate == '44100':
msg.samplerate_default(samplerate)
@@ -168,6 +175,7 @@
'mp3',
'ogg',
'aac',
+ 'opus',
'wav',
'flac'
]
@@ -180,51 +188,58 @@
'ogg'
]
- if codec in codecs_sr and int(samplerate) > 22000 and int(samplerate) <= 27050:
+ if (codec in codecs_sr and int(samplerate) > 22000 and
+ int(samplerate) <= 27050):
samplerate = '22050'
if codec in no96k:
msg.samplerate_no96(codec)
else:
msg.samplerate_info(codec)
- if codec in codecs_sr and int(samplerate) > 27050 and int(samplerate) <= 32000:
+ elif (codec in codecs_sr and int(samplerate) > 27050 and
+ int(samplerate) <= 32000):
samplerate = '32000'
if codec in no96k:
msg.samplerate_no96(codec)
else:
msg.samplerate_info(codec)
- elif codec in codecs_sr and int(samplerate) > 32000 and int(samplerate) <= 36000:
+ elif (codec in codecs_sr and int(samplerate) > 32000 and
+ int(samplerate) <= 36000):
samplerate = '32000'
if codec in no96k:
msg.samplerate_no96(codec)
else:
msg.samplerate_info(codec)
- elif codec in codecs_sr and int(samplerate) > 36000 and int(samplerate) <= 43000:
+ elif (codec in codecs_sr and int(samplerate) > 36000 and
+ int(samplerate) <= 43000):
samplerate = '44100'
if codec in no96k:
msg.samplerate_no96(codec)
else:
msg.samplerate_info(codec)
- if sourceurl == None:
- print(colors.warning('Sample rate has been set to default!'))
+ if sourceurl is None:
+ print(colors.warning('Sample rate set to default!'))
- elif codec in codecs_sr and int(samplerate) > 43000 and int(samplerate) <= 72000:
+ elif (codec in codecs_sr and int(samplerate) > 43000 and
+ int(samplerate) <= 72000):
samplerate = '48000'
if codec in no96k:
msg.samplerate_no96(codec)
else:
msg.samplerate_info(codec)
- elif codec in codecs_sr and int(samplerate) > 72000 and int(samplerate) <= 90000:
+ elif (codec in codecs_sr and int(samplerate) > 72000 and
+ int(samplerate) <= 90000):
samplerate = '88200'
if codec in no96k:
msg.samplerate_no96(codec)
else:
msg.samplerate_info(codec)
- elif codec in codecs_sr and int(samplerate) > 90000 and int(samplerate) <= 96000:
+ elif (codec in codecs_sr and int(samplerate) > 90000 and
+ int(samplerate) <= 96000):
if codec in no96k:
samplerate = '48000'
msg.samplerate_no96(codec)
@@ -232,10 +247,11 @@
samplerate = '96000'
msg.samplerate_info(codec)
- if sourceurl == None:
- print(colors.warning('Sample rate has been set to maximum!'))
+ if sourceurl is None:
+ print(colors.warning('Sample rate set to maximum!'))
- elif codec in codecs_sr and int(samplerate) > 96000 and int(samplerate) <= 176000:
+ elif (codec in codecs_sr and int(samplerate) > 96000 and
+ int(samplerate) <= 176000):
if codec in no96k:
samplerate = '48000'
msg.samplerate_no96(codec)
@@ -243,8 +259,8 @@
samplerate = '176000'
msg.samplerate_info(codec)
- if sourceurl == None:
- print(colors.warning('Sample rate has been set to maximum!'))
+ if sourceurl is None:
+ print(colors.warning('Sample rate set to maximum!'))
elif codec in codecs_sr and int(samplerate) > 176000:
if codec in no96k:
@@ -254,38 +270,41 @@
samplerate = '192000'
msg.samplerate_info(codec)
- if sourceurl == None:
- print(colors.warning('Sample rate has been set to maximum!'))
+ if sourceurl is None:
+ print(colors.warning('Sample rate set to maximum!'))
- if sourceurl == None:
- print(colors.options('Sample rate set to:')+' '+samplerate+'Hz')
+ if sourceurl is None:
+ print(colors.options('Sample rate set to:') + ' ' +
+ samplerate + 'Hz')
"""
We verify platform and other options
"""
platform = mkchromecast.__init__.platform
- def debug_command(): # This function add some more flags to the ffmpeg command
- command.insert(1, '-loglevel') # when user passes --debug option.
+ # This function add some more flags to the ffmpeg command
+ # when user passes --debug option.
+ def debug_command():
+ command.insert(1, '-loglevel')
command.insert(2, 'panic')
return
def modalsa():
command[command.index('pulse')] = 'alsa'
- command[command.index('mkchromecast.monitor')] = adevice
+ command[command.index('Mkchromecast.monitor')] = adevice
print (command)
return
- def set_segmenttime():
- string = [ '-f', 'segment', '-segment_time', str(segmenttime) ]
+ def set_segment_time(position):
+ string = ['-f', 'segment', '-segment_time', str(segment_time)]
for element in string:
- command.insert(-9, element)
+ command.insert(position, element)
return
"""
MP3 192k
"""
- if codec == 'mp3':
+ if codec == 'mp3':
if (platform == 'Linux' and backends_dict[backend] != 'parec' and
backends_dict[backend] != 'gstreamer'):
@@ -293,8 +312,10 @@ def set_segmenttime():
backend,
'-ac', '2',
'-ar', '44100',
+ '-frame_size', str(frame_size),
+ '-fragment_size', str(frame_size),
'-f', 'pulse',
- '-i', 'mkchromecast.monitor',
+ '-i', 'Mkchromecast.monitor',
'-f', 'mp3',
'-acodec', 'libmp3lame',
'-ac', '2',
@@ -302,14 +323,14 @@ def set_segmenttime():
'-b:a', bitrate,
'pipe:'
]
- if adevice != None:
+ if adevice is not None:
modalsa()
- if segmenttime != None:
- set_segmenttime()
+ if segment_time is not None:
+ set_segment_time(-11)
elif (platform == 'Linux' and backends_dict[backend] == 'parec' or
- backends_dict[backend] == 'gstreamer'):
+ backends_dict[backend] == 'gstreamer'):
command = [
'lame',
'-b', bitrate[:-1],
@@ -341,7 +362,7 @@ def set_segmenttime():
command.insert(3, 'device="'+adevice+'"')
else:
command.insert(2, 'pulsesrc')
- command.insert(3, 'device="mkchromecast.monitor"')
+ command.insert(3, 'device="Mkchromecast.monitor"')
"""
else:
command = [
@@ -355,21 +376,24 @@ def set_segmenttime():
'-b:a', bitrate,
'pipe:'
]
- if segmenttime != None:
- set_segmenttime()
+
+ if segment_time is not None:
+ set_segment_time(-11)
"""
OGG 192k
"""
- if codec == 'ogg':
+ if codec == 'ogg':
if (platform == 'Linux' and backends_dict[backend] != 'parec' and
backends_dict[backend] != 'gstreamer'):
command = [
backend,
'-ac', '2',
'-ar', '44100',
+ '-frame_size', str(frame_size),
+ '-fragment_size', str(frame_size),
'-f', 'pulse',
- '-i', 'mkchromecast.monitor',
+ '-i', 'Mkchromecast.monitor',
'-f', 'ogg',
'-acodec', 'libvorbis',
'-ac', '2',
@@ -377,11 +401,11 @@ def set_segmenttime():
'-b:a', bitrate,
'pipe:'
]
- if adevice != None:
+ if adevice is not None:
modalsa()
- if segmenttime != None:
- set_segmenttime()
+ if segment_time is not None:
+ set_segment_time(-11)
elif (platform == 'Linux' and backends_dict[backend] == 'parec' or
backends_dict[backend] == 'gstreamer'):
@@ -411,14 +435,15 @@ def set_segmenttime():
'oggmux',
'!',
'filesink', 'location=/dev/stdout'
- #gst-launch-1.0 pulsesrc device="mkchromecast.monitor" ! audioconvert ! audioresample ! vorbisenc ! oggmux ! filesink
+ #gst-launch-1.0 pulsesrc device="Mkchromecast.monitor"
+ ! audioconvert ! audioresample ! vorbisenc ! oggmux ! filesink
]
if adevice != None:
command.insert(1, 'alsasrc')
command.insert(2, 'device="'+adevice+'"')
else:
command.insert(1, 'pulsesrc')
- command.insert(2, 'device="mkchromecast.monitor"')
+ command.insert(2, 'device="Mkchromecast.monitor"')
"""
else:
command = [
@@ -436,15 +461,17 @@ def set_segmenttime():
"""
AAC > 128k for Stereo, Default sample rate: 44100kHz
"""
- if codec == 'aac':
+ if codec == 'aac':
if (platform == 'Linux' and backends_dict[backend] != 'parec' and
backends_dict[backend] != 'gstreamer'):
command = [
backend,
'-ac', '2',
'-ar', '44100',
+ '-frame_size', str(frame_size),
+ '-fragment_size', str(frame_size),
'-f', 'pulse',
- '-i', 'mkchromecast.monitor',
+ '-i', 'Mkchromecast.monitor',
'-f', 'adts',
'-acodec', 'aac',
'-ac', '2',
@@ -453,7 +480,7 @@ def set_segmenttime():
'-cutoff', '18000',
'pipe:'
]
- if adevice != None:
+ if adevice is not None:
modalsa()
elif (platform == 'Linux' and backends_dict[backend] == 'parec' or
@@ -463,7 +490,7 @@ def set_segmenttime():
'-b', bitrate[:-1],
'-X',
'-P',
- '-c','18000',
+ '-c', '18000',
'-o',
'-',
'-'
@@ -491,7 +518,7 @@ def set_segmenttime():
command.insert(3, 'device="'+adevice+'"')
else:
command.insert(2, 'pulsesrc')
- command.insert(3, 'device="mkchromecast.monitor"')
+ command.insert(3, 'device="Mkchromecast.monitor"')
"""
else:
command = [
@@ -505,37 +532,91 @@ def set_segmenttime():
'-b:a', bitrate,
'pipe:'
]
- if segmenttime != None:
- set_segmenttime()
+
+ if segment_time is not None:
+ set_segment_time(-11)
if platform == 'Darwin':
cutoff = ['-cutoff', '18000']
for element in cutoff:
command.insert(-1, element)
+ """
+ OPUS
+ """
+ if codec == 'opus':
+ if platform == 'Linux' and backends_dict[backend] != 'parec':
+ command = [
+ backend,
+ '-ac', '2',
+ '-ar', '44100',
+ '-frame_size', str(frame_size),
+ '-fragment_size', str(frame_size),
+ '-f', 'pulse',
+ '-i', 'Mkchromecast.monitor',
+ '-f', 'opus',
+ '-acodec', 'libopus',
+ '-ac', '2',
+ '-ar', samplerate,
+ '-b:a', bitrate,
+ 'pipe:'
+ ]
+ if adevice is not None:
+ modalsa()
+
+ if segment_time is not None:
+ set_segment_time(-11)
+
+ elif (platform == 'Linux' and backends_dict[backend] == 'parec' or
+ backends_dict[backend] == 'gstreamer'):
+ command = [
+ 'opusenc',
+ '-',
+ '--raw',
+ '--bitrate', bitrate[:-1],
+ '--raw-rate', samplerate,
+ '-'
+ ]
+ else:
+ command = [
+ backend,
+ '-f', 'avfoundation',
+ '-i', ':Soundflower (2ch)',
+ '-f', 'opus',
+ '-acodec', 'libopus',
+ '-ac', '2',
+ '-ar', samplerate,
+ '-b:a', bitrate,
+ 'pipe:'
+ ]
+ if segment_time is not None:
+ set_segment_time(-11)
"""
WAV 24-Bit
"""
- if codec == 'wav':
+ if codec == 'wav':
if platform == 'Linux' and backends_dict[backend] != 'parec':
command = [
backend,
'-ac', '2',
'-ar', '44100',
+ '-frame_size', str(frame_size),
+ '-fragment_size', str(frame_size),
'-f', 'pulse',
- '-i', 'mkchromecast.monitor',
+ '-i', 'Mkchromecast.monitor',
'-f', 'wav',
'-acodec', 'pcm_s24le',
'-ac', '2',
'-ar', samplerate,
'pipe:'
]
- if adevice != None:
+
+ if adevice is not None:
modalsa()
- if segmenttime != None:
- set_segmenttime()
+ if segment_time is not None:
+ set_segment_time(-9)
elif (platform == 'Linux' and backends_dict[backend] == 'parec' or
backends_dict[backend] == 'gstreamer'):
@@ -565,20 +646,23 @@ def set_segmenttime():
'-ar', samplerate,
'pipe:'
]
- if segmenttime != None:
- set_segmenttime()
+ if segment_time is not None:
+ set_segment_time(-9)
"""
- FLAC 24-Bit (values taken from: https://trac.ffmpeg.org/wiki/Encode/HighQualityAudio) except for parec.
+ FLAC 24-Bit (values taken from:
+ https://trac.ffmpeg.org/wiki/Encode/HighQualityAudio) except for parec.
"""
- if codec == 'flac':
+ if codec == 'flac':
if platform == 'Linux' and backends_dict[backend] != 'parec':
command = [
backend,
'-ac', '2',
'-ar', '44100',
+ '-frame_size', str(frame_size),
+ '-fragment_size', str(frame_size),
'-f', 'pulse',
- '-i', 'mkchromecast.monitor',
+ '-i', 'Mkchromecast.monitor',
'-f', 'flac',
'-acodec', 'flac',
'-ac', '2',
@@ -586,11 +670,11 @@ def set_segmenttime():
'-b:a', bitrate,
'pipe:'
]
- if adevice != None:
+ if adevice is not None:
modalsa()
- if segmenttime != None:
- set_segmenttime()
+ if segment_time is not None:
+ set_segment_time(-11)
elif (platform == 'Linux' and backends_dict[backend] == 'parec' or
backends_dict[backend] == 'gstreamer'):
@@ -617,18 +701,19 @@ def set_segmenttime():
'-b:a', bitrate,
'pipe:'
]
- if segmenttime != None:
- set_segmenttime()
+ if segment_time is not None:
+ set_segment_time(-11)
verbose_backend = ['ffmpeg', 'avconv']
- if debug == False and backends_dict[backend] in verbose_backend:
+ if debug is False and backends_dict[backend] in verbose_backend:
debug_command()
app = Flask(__name__)
-if debug == True:
+if debug is True:
print(':::audio::: command '+str(command))
+
@app.route('/')
def index():
return """
@@ -656,20 +741,20 @@ def shutdown():
return 'Server shutting down...'
"""
+
+
@app.route('/' + appendtourl)
def stream():
- if (platform == 'Linux'
- and bool(backends_dict) == True
- and backends_dict[backend] == 'parec'):
+ if (platform == 'Linux' and bool(backends_dict) is True and
+ backends_dict[backend] == 'parec'):
c_parec = [
backend,
'--format=s16le',
- '-d', 'mkchromecast.monitor'
+ '-d', 'Mkchromecast.monitor'
]
parec = Popen(c_parec, stdout=PIPE)
process = Popen(command, stdin=parec.stdout, stdout=PIPE, bufsize=-1)
- elif (platform == 'Linux' and
- bool(backends_dict) == True and
+ elif (platform == 'Linux' and bool(backends_dict) is True and
backends_dict[backend] == 'gstreamer'):
c_gst = [
'gst-launch-1.0',
@@ -679,19 +764,20 @@ def stream():
'!',
'filesink', 'location=/dev/stdout'
]
- if adevice != None:
+ if adevice is not None:
c_gst.insert(2, 'alsasrc')
- c_gst.insert(3, 'device="'+adevice+'"')
+ c_gst.insert(3, 'device="' + adevice + '"')
else:
c_gst.insert(2, 'pulsesrc')
- c_gst.insert(3, 'device="mkchromecast.monitor"')
+ c_gst.insert(3, 'device="Mkchromecast.monitor"')
gst = Popen(c_gst, stdout=PIPE)
process = Popen(command, stdin=gst.stdout, stdout=PIPE, bufsize=-1)
else:
process = Popen(command, stdout=PIPE, bufsize=-1)
- read_chunk = partial(os.read, process.stdout.fileno(), chunk_size)
+ read_chunk = partial(os.read, process.stdout.fileno(), buffer_size)
return Response(iter(read_chunk, b''), mimetype=mtype)
+
def start_app():
"""Starting the streaming server.
@@ -700,7 +786,8 @@ def start_app():
"""
monitor_daemon = monitor()
monitor_daemon.start()
- app.run(host= '0.0.0.0', passthrough_errors=False)
+ app.run(host='0.0.0.0', port=port, passthrough_errors=False)
+
class multi_proc(object): # I launch ffmpeg in a different process
def __init__(self):
@@ -709,13 +796,14 @@ def __init__(self):
def start(self):
self.proc.start()
-"""
-I create a class to launch a thread in this process that monitors if main
-application stops.
-A normal running of mkchromecast will have 2 threads in the streaming process
-when ffmpeg is used.
-"""
+
+
class monitor(object):
+ """
+ I create a class to launch a thread in this process that monitors if main
+ application stops. A normal running of mkchromecast will have 2 threads in
+ the streaming process when ffmpeg is used.
+ """
def __init__(self):
self.monitor_d = threading.Thread(target=monitor_daemon)
self.monitor_d.daemon = True
@@ -723,26 +811,29 @@ def __init__(self):
def start(self):
self.monitor_d.start()
+
def monitor_daemon():
f = open('/tmp/mkchromecast.pid', 'rb')
- pidnumber=int(pickle.load(f))
- print(colors.options('PID of main process:')+' '+str(pidnumber))
+ pidnumber = int(pickle.load(f))
+ print(colors.options('PID of main process:') + ' ' + str(pidnumber))
- localpid=getpid()
- print(colors.options('PID of streaming process:')+' '+str(localpid))
+ localpid = getpid()
+ print(colors.options('PID of streaming process:') + ' ' + str(localpid))
- while psutil.pid_exists(localpid) == True:
+ while psutil.pid_exists(localpid) is True:
try:
time.sleep(0.5)
- if psutil.pid_exists(pidnumber) == False: # With this I ensure that if the main app fails, everything
- if platform == 'Darwin': # will get back to normal
+ # With this I ensure that if the main app fails, everything
+ # will get back to normal
+ if psutil.pid_exists(pidnumber) is False:
+ if platform == 'Darwin':
inputint()
outputint()
else:
from mkchromecast.pulseaudio import remove_sink
remove_sink()
parent = psutil.Process(localpid)
- for child in parent.children(recursive=True): # or parent.children() for recursive=False
+ for child in parent.children(recursive=True):
child.kill()
parent.kill()
except KeyboardInterrupt:
@@ -755,6 +846,7 @@ def monitor_daemon():
print('OSError')
sys.exit(0)
+
def main():
st = multi_proc()
st.start()
diff --git a/mkchromecast/audio_devices.py b/mkchromecast/audio_devices.py
index e02f7998a..ff8787d2b 100644
--- a/mkchromecast/audio_devices.py
+++ b/mkchromecast/audio_devices.py
@@ -2,7 +2,6 @@
# This file is part of mkchromecast.
-import socket
import subprocess
import os.path
@@ -14,34 +13,38 @@
name()
"""
+
def inputdev():
- if os.path.exists('./bin/audiodevice') == True:
+ if os.path.exists('./bin/audiodevice') is True:
inputdevtosf2 = ['./bin/audiodevice input "Soundflower (2ch)"']
else:
inputdevtosf2 = ['./audiodevice input "Soundflower (2ch)"']
subprocess.Popen(inputdevtosf2, shell=True)
return
+
def outputdev():
- if os.path.exists('./bin/audiodevice') == True:
+ if os.path.exists('./bin/audiodevice') is True:
outputdevtosf2 = ['./bin/audiodevice output "Soundflower (2ch)"']
else:
outputdevtosf2 = ['./audiodevice output "Soundflower (2ch)"']
subprocess.Popen(outputdevtosf2, shell=True)
return
+
def inputint():
- if os.path.exists('./bin/audiodevice') == True:
- inputinttosf2 = ['./bin/audiodevice input internal']
+ if os.path.exists('./bin/audiodevice') is True:
+ inputinttosf2 = ['./bin/audiodevice input "Internal Microphone"']
else:
- inputinttosf2 = ['./audiodevice input internal']
+ inputinttosf2 = ['./audiodevice input "Internal Microphone"']
subprocess.call(inputinttosf2, shell=True)
return
+
def outputint():
- if os.path.exists('./bin/audiodevice') == True:
- outputinttosf2 = ['./bin/audiodevice output internal']
+ if os.path.exists('./bin/audiodevice') is True:
+ outputinttosf2 = ['./bin/audiodevice output "Internal Speakers"']
else:
- outputinttosf2 = ['./audiodevice output internal']
+ outputinttosf2 = ['./audiodevice output "Internal Speakers"']
subprocess.call(outputinttosf2, shell=True)
return
diff --git a/mkchromecast/cast.py b/mkchromecast/cast.py
index a5ac551fb..99f7ba7ed 100644
--- a/mkchromecast/cast.py
+++ b/mkchromecast/cast.py
@@ -3,10 +3,9 @@
# This file is part of mkchromecast.
from __future__ import print_function
-from mkchromecast.__init__ import *
-from mkchromecast.audio_devices import *
+from mkchromecast.audio_devices import inputint, outputint
import mkchromecast.colors as colors
-from mkchromecast.config import *
+from mkchromecast.__init__ import checkmktmp, args
from mkchromecast.preferences import ConfigSectionMap
from mkchromecast.utils import terminate
from mkchromecast.pulseaudio import remove_sink
@@ -17,7 +16,6 @@
import os.path
import pickle
import subprocess
-import mkchromecast.colors as colors
from threading import Thread
"""
@@ -26,7 +24,7 @@
try:
import ConfigParser
except ImportError:
- import configparser as ConfigParser # This is for Python3
+ import configparser as ConfigParser # This is for Python3
"""
We verify that soco is installed to give Sonos support
@@ -41,8 +39,9 @@
class casting(object):
"""Main casting class.
"""
- def __init__(self): # __init__ to call the self.ip
- import mkchromecast.__init__ # This is to verify against some needed variables
+ def __init__(self):
+ # This is to verify against some needed variables
+ import mkchromecast.__init__
self.platform = mkchromecast.__init__.platform
self.tray = mkchromecast.__init__.tray
self.select_cc = mkchromecast.__init__.select_cc
@@ -53,10 +52,13 @@ def __init__(self): # __init__ to call the self.ip
self.discover = mkchromecast.__init__.discover
self.host = mkchromecast.__init__.host
self.ccname = mkchromecast.__init__.ccname
- self.reconnect = mkchromecast.__init__.reconnect
+ self.hijack = mkchromecast.__init__.hijack
+ self.tries = mkchromecast.__init__.tries
+ self.port = mkchromecast.__init__.port
+ self.port = str(self.port)
self.title = 'Mkchromecast v' + mkchromecast.__init__.__version__
- if self.host == None:
+ if self.host is None:
if self.platform == 'Linux':
self.getnetworkip()
try:
@@ -66,7 +68,7 @@ def __init__(self): # __init__ to call the self.ip
else:
try:
self.ip = socket.gethostbyname(socket.gethostname())
- if self.debug == True:
+ if self.debug is True:
print(':::cast::: sockets method', self.ip)
except socket.gaierror:
self.netifaces_ip()
@@ -88,7 +90,7 @@ def getnetworkip(self):
except socket.error:
self.ip = '127.0.0.1'
self.discovered_ip = s.getsockname()[0]
- if self.debug == True:
+ if self.debug is True:
print(':::cast::: sockets method', self.discovered_ip)
def netifaces_ip(self):
@@ -98,18 +100,21 @@ def netifaces_ip(self):
if interface == 'lo':
continue
iface = netifaces.ifaddresses(interface).get(netifaces.AF_INET)
- if iface != None and iface[0]['addr'] != '127.0.0.1':
+ if iface is not None and iface[0]['addr'] is not '127.0.0.1':
for e in iface:
self.discovered_ip = str(e['addr'])
- if self.debug == True:
- print(':::cast::: netifaces method', self.discovered_ip)
+ if self.debug is True:
+ print(':::cast::: netifaces method',
+ self.discovered_ip)
def _get_chromecasts(self):
# compatibility
try:
return list(pychromecast.get_chromecasts_as_dict().keys())
except AttributeError:
- self._chromecasts_by_name = {c.name: c for c in pychromecast.get_chromecasts()}
+ self._chromecasts_by_name = {c.name: c for c in
+ pychromecast.get_chromecasts(
+ tries=self.tries)}
return list(self._chromecasts_by_name.keys())
def _get_chromecast(self, name):
@@ -123,102 +128,100 @@ def _get_chromecast(self, name):
Cast processes
"""
def initialize_cast(self):
- import mkchromecast.__init__ # This is to verify against some needed variables.
- from pychromecast import socket_client # This fixes the `No handlers could be found for logger "pychromecast.socket_client` warning"`.
- # See commit 18005ebd4c96faccd69757bf3d126eb145687e0d.
+ # This fixes the `No handlers could be found for logger
+ # "pychromecast.socket_client` warning"`.
+ # See commit 18005ebd4c96faccd69757bf3d126eb145687e0d.
+ from pychromecast import socket_client
self.cclist = self._get_chromecasts()
- self.cclist = [ [index, _, 'Gcast'] for index, _ in enumerate(self.cclist) ]
+ self.cclist = [[i, _, 'Gcast'] for i, _ in enumerate(self.cclist)]
- if sonos == True:
+ if sonos is True:
try:
self.sonos_list = list(soco.discover())
- length = len(self.cclist)
for self.index, device in enumerate(self.sonos_list):
add_sonos = [self.index, device, 'Sonos']
self.cclist.append(add_sonos)
except TypeError:
pass
- if self.debug == True:
+ if self.debug is True:
print('self.cclist', self.cclist)
- """
- This block was used for casting youtube with pychromecast, but it does not work
- """
- """
- try:
- self.youtubeurl = mkchromecast.__init__.youtubeurl
- except AttributeError:
- self.youtubeurl = None
- """
-
- if (len(self.cclist) != 0 and self.select_cc == False and
- self.ccname == None):
- if self.debug == True:
+ if (len(self.cclist) != 0 and self.select_cc is False and
+ self.ccname is None):
+ if self.debug is True:
print('if len(self.cclist) != 0 and self.select_cc == False:')
print(' ')
- print(colors.important('List of Devices Available in your Network:'))
- print(colors.important('------------------------------------------\n'))
- print(colors.important('Index \tTypes \tFriendly Name '))
- print(colors.important('===== \t===== \t============= '))
+ print(colors.important('List of Devices Available in Network:'))
+ print(colors.important('-------------------------------------\n'))
+ print(colors.important('Index Types Friendly Name '))
+ print(colors.important('===== ===== ============= '))
self.availablecc()
print(' ')
- if self.discover == False:
- print(colors.important('We will cast to first device in the list above!'))
+ if self.discover is False:
+ print(colors.important('Casting to first device shown above!'))
+ print(colors.important('Select devices by using the -s flag.'))
print(' ')
self.cast_to = self.cclist[0][1]
- print(colors.success(self.cast_to))
+ if self.cclist[0][2] == 'Sonos':
+ print(colors.success(self.cast_to.player_name))
+ else:
+ print(colors.success(self.cast_to))
print(' ')
- elif (len(self.cclist) != 0 and self.select_cc == True and
- self.tray == False and self.ccname == None):
- if self.debug == True:
- print('elif len(self.cclist) != 0 and self.select_cc == True and self.tray == False:')
- if os.path.exists('/tmp/mkchromecast.tmp') == False:
+ elif (len(self.cclist) != 0 and self.select_cc is True and
+ self.tray is False and self.ccname is None):
+ if self.debug is True:
+ print('elif len(self.cclist) != 0 and self.select_cc == True'
+ ' and self.tray == False:')
+ if os.path.exists('/tmp/mkchromecast.tmp') is False:
self.tf = open('/tmp/mkchromecast.tmp', 'wb')
print(' ')
- print(colors.important('List of Devices Available in your Network:'))
- print(colors.important('------------------------------------------\n'))
- print(colors.important('Index \tTypes \tFriendly Name '))
- print(colors.important('===== \t===== \t============= '))
+ print(colors.important('List of Devices Available in Network:'))
+ print(colors.important('-------------------------------------\n'))
+ print(colors.important('Index Types Friendly Name '))
+ print(colors.important('===== ===== ============= '))
self.availablecc()
else:
- if self.debug == True:
+ if self.debug is True:
print('else:')
self.tf = open('/tmp/mkchromecast.tmp', 'rb')
- self.index=pickle.load(self.tf)
+ self.index = pickle.load(self.tf)
self.cast_to = self.cclist[int(self.index)]
print(' ')
- print(colors.options('Casting to:')+' '+colors.success(self.cast_to))
+ print(colors.options('Casting to:') + ' ' +
+ colors.success(self.cast_to))
print(' ')
- elif (len(self.cclist) != 0 and self.select_cc == True and
- self.tray == True):
- if self.debug == True:
- print('elif len(self.cclist) != 0 and self.select_cc == True and self.tray == True:')
- if os.path.exists('/tmp/mkchromecast.tmp') == False:
+ elif (len(self.cclist) != 0 and self.select_cc is True and
+ self.tray is True):
+ if self.debug is True:
+ print('elif len(self.cclist) != 0 and self.select_cc == True'
+ ' and self.tray == True:')
+ if os.path.exists('/tmp/mkchromecast.tmp') is False:
self.tf = open('/tmp/mkchromecast.tmp', 'wb')
print(' ')
- print(colors.important('List of Devices Available in your Network:'))
- print(colors.important('------------------------------------------\n'))
- print(colors.important('Index \tTypes \tFriendly Name '))
- print(colors.important('===== \t===== \t============= '))
+ print(colors.important('List of Devices Available in Network:'))
+ print(colors.important('-------------------------------------\n'))
+ print(colors.important('Index Types Friendly Name '))
+ print(colors.important('===== ===== ============= '))
self.availablecc()
else:
- if self.debug == True:
+ if self.debug is True:
print('else:')
self.tf = open('/tmp/mkchromecast.tmp', 'rb')
- self.cast_to=pickle.load(self.tf)
+ self.cast_to = pickle.load(self.tf)
self.availablecc()
print(' ')
- print(colors.options('Casting to:')+' '+colors.success(self.cast_to))
+ print(colors.options('Casting to:') + ' ' +
+ colors.success(self.cast_to))
print(' ')
- elif len(self.cclist) == 0 and self.tray == False:
- if self.debug == True:
+ elif len(self.cclist) == 0 and self.tray is False:
+ if self.debug is True:
print('elif len(self.cclist) == 0 and self.tray == False:')
print(colors.error('No devices found!'))
- if self.platform == 'Linux' and self.adevice == None:
+ if self.platform == 'Linux' and self.adevice is None:
remove_sink()
elif self.platform == 'Darwin':
inputint()
@@ -226,13 +229,14 @@ def initialize_cast(self):
terminate()
exit()
- elif len(self.cclist) == 0 and self.tray == True:
+ elif len(self.cclist) == 0 and self.tray is True:
print(colors.error(':::Tray::: No devices found!'))
self.availablecc = []
def sel_cc(self):
print(' ')
- print('Please, select the ' + colors.important('Index') + ' of the Google Cast device that you want to use:')
+ print('Please, select the ' + colors.important('Index') +
+ ' of the Google Cast device that you want to use:')
self.index = input()
def inp_cc(self):
@@ -242,10 +246,12 @@ def inp_cc(self):
self.tf.close()
self.cast_to = self.cclist[int(self.index)][1]
print(' ')
- print(colors.options('Casting to:') + ' ' + colors.success(self.cast_to))
+ print(colors.options('Casting to:') + ' ' +
+ colors.success(self.cast_to))
print(' ')
except TypeError:
- print(colors.options('Casting to:') + ' ' + colors.success(self.cast_to.player_name))
+ print(colors.options('Casting to:') + ' ' +
+ colors.success(self.cast_to.player_name))
except IndexError:
checkmktmp()
self.tf = open('/tmp/mkchromecast.tmp', 'wb')
@@ -254,31 +260,35 @@ def inp_cc(self):
break
def get_cc(self):
- if self.debug == True:
+ if self.debug is True:
print('def get_cc(self):')
try:
- if self.ccname != None:
+ if self.ccname is not None:
self.cast_to = self.ccname
self.cast = self._get_chromecast(self.cast_to)
# Wait for cast device to be ready
self.cast.wait()
print(' ')
- print(colors.important('Information about ') + ' ' + colors.success(self.cast_to))
+ print(colors.important('Information about ') + ' ' +
+ colors.success(self.cast_to))
print(' ')
print(self.cast.device)
print(' ')
- print(colors.important('Status of device ') + ' ' + colors.success(self.cast_to))
+ print(colors.important('Status of device ') + ' ' +
+ colors.success(self.cast_to))
print(' ')
print(self.cast.status)
print(' ')
except pychromecast.error.NoChromecastFoundError:
- print(colors.error('No Chromecasts matching filter criteria were found!'))
+ print(colors.error('No Chromecasts matching filter criteria'
+ ' were found!'))
if self.platform == 'Darwin':
inputint()
outputint()
elif self.platform == 'Linux':
remove_sink()
- if self.tray == False: # In the case that the tray is used, we don't kill the application
+ # In the case that the tray is used, we don't kill the application
+ if self.tray is False:
print(colors.error('Finishing the application...'))
terminate()
exit()
@@ -290,82 +300,100 @@ def get_cc(self):
pass
def play_cast(self):
- if self.debug == True:
+ if self.debug is True:
print('def play_cast(self):')
localip = self.ip
try:
- print(colors.options('The IP of ')
- + colors.success(self.cast_to)+colors.options(' is:')
- + ' ' + self.cast.host)
+ print(colors.options('The IP of ') +
+ colors.success(self.cast_to) + colors.options(' is:') +
+ ' ' + self.cast.host)
except TypeError:
- print(colors.options('The IP of ')
- + colors.success(self.cast_to.player_name)
- + colors.options(' is:') + ' ' + self.cast_to.ip_address)
+ print(colors.options('The IP of ') +
+ colors.success(self.cast_to.player_name) +
+ colors.options(' is:') + ' ' + self.cast_to.ip_address)
except AttributeError:
for _ in self.sonos_list:
if self.cast_to in _.player_name:
self.cast_to = _
- print(colors.options('The IP of ')
- + colors.success(self.cast_to.player_name)
- + colors.options(' is:') + ' ' + self.cast_to.ip_address)
+ print(colors.options('The IP of ') +
+ colors.success(self.cast_to.player_name) +
+ colors.options(' is:') + ' ' + self.cast_to.ip_address)
- if self.host == None:
+ if self.host is None:
print(colors.options('Your local IP is:') + ' ' + localip)
else:
- print(colors.options('Your manually entered local IP is:') + ' ' + localip)
+ print(colors.options('Your manually entered local IP is:') +
+ ' ' + localip)
try:
media_controller = self.cast.media_controller
- if self.tray == True:
+ if self.tray is True:
config = ConfigParser.RawConfigParser()
- configurations = config_manager() # Class from mkchromecast.config
+ # Class from mkchromecast.config
+ from mkchromecast.config import config_manager
+ configurations = config_manager()
configf = configurations.configf
- if os.path.exists(configf) and self.tray == True:
- print(tray)
+ if os.path.exists(configf) and self.tray is True:
+ print(self.tray)
print(colors.warning('Configuration file exists'))
print(colors.warning('Using defaults set there'))
config.read(configf)
self.backend = ConfigSectionMap('settings')['backend']
- if self.sourceurl != None:
- if args.video == True:
+ if self.sourceurl is not None:
+ if args.video is True:
import mkchromecast.video
mtype = mkchromecast.video.mtype
else:
import mkchromecast.audio
mtype = mkchromecast.audio.mtype
print(' ')
- print(colors.options('Casting from stream URL:') + ' ' + self.sourceurl)
- print(colors.options('Using media type:') + ' ' + mtype)
- media_controller.play_media(self.sourceurl, mtype, title=self.title)
+ print(colors.options('Casting from stream URL:') + ' ' +
+ self.sourceurl)
+ print(colors.options('Using media type:') + ' ' +
+ mtype)
+ media_controller.play_media(
+ self.sourceurl,
+ mtype,
+ title=self.title
+ )
elif (self.backend == 'ffmpeg' or self.backend == 'node' or
self.backend == 'avconv' or self.backend == 'parec' or
- self.backend == 'gstreamer' and self.sourceurl == None):
- if args.video == True:
+ self.backend == 'gstreamer' and self.sourceurl is None):
+ if args.video is True:
import mkchromecast.video
mtype = mkchromecast.video.mtype
else:
import mkchromecast.audio
mtype = mkchromecast.audio.mtype
print(' ')
- print(colors.options('The media type string used is:') + ' ' + mtype)
- media_controller.play_media('http://' + localip + ':5000/stream', mtype, title=self.title)
+ print(colors.options('The media type string used is:') +
+ ' ' + mtype)
+ media_controller.play_media(
+ 'http://' + localip + ':' + self.port + '/stream',
+ mtype,
+ title=self.title
+ )
print(' ')
print(colors.important('Cast media controller status'))
print(' ')
print(self.cast.status)
print(' ')
- if self.reconnect == True:
- self.r = Thread(target=self.reconnect_cc)
- self.r.daemon = True # This has to be set to True so that we catch KeyboardInterrupt.
+ if self.hijack is True:
+ self.r = Thread(target=self.hijack_cc)
+ # This has to be set to True so that we catch
+ # KeyboardInterrupt.
+ self.r.daemon = True
self.r.start()
except AttributeError:
self.sonos = self.cast_to
- self.sonos.play_uri('x-rincon-mp3radio://' + localip + ':5000/stream', title=self.title)
- if self.tray == True:
+ self.sonos.play_uri('x-rincon-mp3radio://' + localip +
+ ':' + self.port + '/stream',
+ title=self.title)
+ if self.tray is True:
self.cast = self.sonos
def stop_cast(self):
@@ -378,7 +406,8 @@ def volume_up(self):
""" Increment volume by 0.1 unless it is already maxed.
Returns the new volume.
"""
- print('Increasing volume... \n')
+ if self.debug is True:
+ print('Increasing volume... \n')
try:
volume = round(self.cast.status.volume_level, 1)
return self.cast.set_volume(volume + 0.1)
@@ -390,7 +419,8 @@ def volume_down(self):
""" Decrement the volume by 0.1 unless it is already 0.
Returns the new volume.
"""
- print('Decreasing volume... \n')
+ if self.debug is True:
+ print('Decreasing volume... \n')
try:
volume = round(self.cast.status.volume_level, 1)
return self.cast.set_volume(volume - 0.1)
@@ -426,7 +456,9 @@ def availablecc(self):
device = device[1].player_name
else:
device = device[1]
- print('%s \t%s \t%s' % (self.index, device[2], str(unicode(device).encode("utf-8"))))
+ print('%s \t%s \t%s' %
+ (self.index, device[2],
+ str(unicode(device).encode("utf-8"))))
if types == 'Sonos':
to_append = [self.index, device, types, device_ip]
@@ -434,8 +466,8 @@ def availablecc(self):
to_append = [self.index, device, types]
self.availablecc.append(to_append)
- def reconnect_cc(self):
- """Dummy method to call _reconnect_cc_().
+ def hijack_cc(self):
+ """Dummy method to call _hijack_cc_().
In the cast that the self.r thread is alive, we check that the
chromecast is connected. If it is connected, we check again in
@@ -443,26 +475,27 @@ def reconnect_cc(self):
"""
try:
while self.r.is_alive():
- self._reconnect_cc_()
- time.sleep(5) #FIXME: I think that this has to be set by users.
+ self._hijack_cc_()
+ # FIXME: I think that this has to be set by users.
+ time.sleep(5)
except KeyboardInterrupt:
self.stop_cast()
- if platform == 'Darwin':
+ if self.platform == 'Darwin':
inputint()
outputint()
- elif platform == 'Linux' and adevice == None:
+ elif self.platform == 'Linux' and self.adevice is None:
remove_sink()
terminate()
- def _reconnect_cc_(self):
- """Check if chromecast is disconnected and reconnect.
+ def _hijack_cc_(self):
+ """Check if chromecast is disconnected and hijack.
This function checks if the chromecast is online. Then, if the display
- name is different from "Default Media Receiver", it reconnects to the
+ name is different from "Default Media Receiver", it hijacks to the
chromecast.
"""
ip = self.cast.host
- if ping_chromecast(ip) == True: # The chromecast is online.
+ if ping_chromecast(ip) is True: # The chromecast is online.
if str(self.cast.status.display_name) != "Default Media Receiver":
self.ccname = self.cast_to
self.get_cc()
@@ -475,13 +508,14 @@ def _reconnect_cc_(self):
except AttributeError:
pass
+
def ping_chromecast(ip):
"""This function pings to hosts.
Credits: http://stackoverflow.com/a/34455969/1995261
"""
try:
- output = subprocess.check_output("ping -c 1 " + ip, shell=True)
+ subprocess.check_output("ping -c 1 " + ip, shell=True)
except:
return False
return True
diff --git a/mkchromecast/colors.py b/mkchromecast/colors.py
index 2f30e9d69..b01f3ce9d 100644
--- a/mkchromecast/colors.py
+++ b/mkchromecast/colors.py
@@ -8,6 +8,7 @@
except NameError:
unicode = str # Python 3
+
def color(text, **user_styles):
styles = {
# styles
@@ -50,7 +51,8 @@ def color(text, **user_styles):
try:
color_text += styles[style]
except KeyError:
- raise KeyError('def color: parameter `{}` does not exist'.format(style))
+ raise KeyError('def color: parameter `{}` does not exist'
+ .format(style))
color_text += text
try:
@@ -58,20 +60,26 @@ def color(text, **user_styles):
except UnicodeEncodeError:
return '\033[0m{}\033[0m'.format(unicode(color_text).encode("utf-8"))
+
def error(text):
return color(text, bold=True, fg_red=True)
+
def important(text):
return color(text, bold=False, fg_blue=True)
+
def options(text):
return color(text, underline=True)
+
def bold(text):
return color(text, bold=True)
+
def warning(text):
return color(text, fg_orange=True)
+
def success(text):
return color(text, fg_green=True)
diff --git a/mkchromecast/config.py b/mkchromecast/config.py
index 9414a88af..dda5c8682 100644
--- a/mkchromecast/config.py
+++ b/mkchromecast/config.py
@@ -4,16 +4,18 @@
"""
Configparser is imported differently in Python3
"""
-import mkchromecast.__init__ # This is to verify against some needed variables
+import os
+import getpass
+import mkchromecast.__init__
try:
import ConfigParser
except ImportError:
- import configparser as ConfigParser # This is for Python3
-import os, getpass
+ import configparser as ConfigParser # Python3
platform = mkchromecast.__init__.platform
debug = mkchromecast.__init__.debug
+
class config_manager(object):
def __init__(self):
self.user = getpass.getuser()
@@ -38,14 +40,16 @@ def __init__(self):
# Writing our configuration file
"""
- Depeding the platform we create the configuration directory in different
- locations.
+ Depeding the platform we create the configuration directory in
+ different locations.
"""
if platform == 'Darwin':
- self.directory = '/Users/'+self.user+'/Library/Application Support/mkchromecast/'
+ self.directory = '/Users/' + self.user + \
+ '/Library/Application Support/mkchromecast/'
else:
- self.directory = '/home/'+self.user+'/.config/mkchromecast/' #Linux
- self.configf = self.directory+'mkchromecast.cfg'
+ self.directory = '/home/' + self.user + \
+ '/.config/mkchromecast/' # Linux
+ self.configf = self.directory + 'mkchromecast.cfg'
def config_defaults(self):
"""
@@ -105,16 +109,17 @@ def chk_config(self):
try:
e = ConfigSectionMap('settings')[str(e)]
except KeyError:
- if debug == True:
- print(':::config::: the setting '+e+' is not correctly set. Defaults added.')
+ if debug is True:
+ print(':::config::: the setting %s is not correctly set. '
+ 'Defaults added.' % e)
self.config.set('settings', str(e), self.defaultconf[e])
with open(self.configf, 'w') as configfile:
self.config.write(configfile)
backend = ConfigSectionMap('settings')['backend']
- codec= ConfigSectionMap('settings')['codec']
+ codec = ConfigSectionMap('settings')['codec']
bitrate = ConfigSectionMap('settings')['bitrate']
- samplerate= ConfigSectionMap('settings')['samplerate']
+ samplerate = ConfigSectionMap('settings')['samplerate']
notifications = ConfigSectionMap('settings')['notifications']
colors = ConfigSectionMap('settings')['colors']
searchatlaunch = ConfigSectionMap('settings')['searchatlaunch']
@@ -123,7 +128,9 @@ def chk_config(self):
codecs = [
'mp3',
'ogg',
- 'aac'
+ 'aac',
+ 'opus',
+ 'flac',
]
if os.path.exists(self.configf):
@@ -135,9 +142,11 @@ def chk_config(self):
self.config.set('settings', 'codec', str(codec))
self.config.set('settings', 'bitrate', '192')
self.config.set('settings', 'samplerate', str(samplerate))
- self.config.set('settings', 'notifications', str(notifications))
+ self.config.set('settings', 'notifications',
+ str(notifications))
self.config.set('settings', 'colors', str(colors))
- self.config.set('settings', 'searchatlaunch', str(searchatlaunch))
+ self.config.set('settings', 'searchatlaunch',
+ str(searchatlaunch))
self.config.set('settings', 'alsadevice', str(alsadevice))
with open(self.configf, 'w') as configfile:
diff --git a/mkchromecast/messages.py b/mkchromecast/messages.py
index a059ba169..e99c55d7b 100644
--- a/mkchromecast/messages.py
+++ b/mkchromecast/messages.py
@@ -6,58 +6,63 @@
sourceurl = mkchromecast.__init__.sourceurl
-"""
-Bitrate messages
-"""
+
def bitrate_default(bitrate):
- """Printing default bitrate message"""
- if sourceurl == None:
- print(colors.options('Default bitrate used:')+' '+ bitrate)
+ """Bitrate messages
+ Printing default bitrate message
+ """
+ if sourceurl is None:
+ print(colors.options('Default bitrate used:') + ' ' + bitrate)
return
+
def no_bitrate(codec):
- if sourceurl == None:
- print(colors.warning('The '+codec+' codec does not require the bitrate argument.'))
+ if sourceurl is None:
+ print(colors.warning('The ' + codec +
+ ' codec does not require the bitrate argument.'))
return
+
def maxbitrate(codec, bitrate):
- if sourceurl == None:
- print(colors.warning('Maximum bitrate supported by '+codec+' is: '+bitrate+'k.'))
- if codec == 'aac' and sourceurl == None:
- print(colors.warning('At about 128-256k is already considered as "transparent" for '+codec+'.'))
+ if sourceurl is None:
+ print(colors.warning('Maximum bitrate supported by ' + codec +
+ ' is: ' + bitrate + 'k.'))
+ if codec == 'aac' and sourceurl is None:
+ print(colors.warning('At about 128-256k is already considered as '
+ '"transparent" for ' + codec + '.'))
print(colors.warning('You may try lossless audio coding formats.'))
print(colors.warning('Bitrate has been set to maximum!'))
return
-"""
-Sample rate messages
-"""
+
def samplerate_default(samplerate):
- """Printing default sample rate message"""
- if sourceurl == None:
- print(colors.options('Default sample rate used:')+' '+ samplerate+'Hz')
+ """Sample rate messages
+ Printing default sample rate message"""
+ if sourceurl is None:
+ print(colors.options('Default sample rate used:') + ' ' +
+ samplerate + 'Hz.')
return
+
def samplerate_info(codec):
"""This prints warning when sample rates are set incorrectly"""
- if sourceurl == None:
- print(colors.warning('Sample rates supported by '+codec+' are: '
- +str(22050)+'Hz, '
- +str(32000)+'Hz, '
- +str(44100)+'Hz, '
- +str(48000)+'Hz '
- +str(96000)+'Hz or '
- +str(192000)+'Hz')
- )
+ if sourceurl is None:
+ print(colors.warning('Sample rates supported by ' + codec + ' are: ' +
+ str(22050) + 'Hz, ' +
+ str(32000) + 'Hz, ' +
+ str(44100) + 'Hz, ' +
+ str(48000) + 'Hz ' +
+ str(96000) + 'Hz or ' +
+ str(192000) + 'Hz.'))
return
+
def samplerate_no96(codec):
"""This prints warning when sample rates are set incorrectly and no 96k"""
- if sourceurl == None:
- print(colors.warning('sample rates supported by '+codec+' are: '
- +str(22050)+'hz, '
- +str(32000)+'hz, '
- +str(44100)+'hz or, '
- +str(48000)+'hz')
- )
+ if sourceurl is None:
+ print(colors.warning('Sample rates supported by ' + codec + ' are: ' +
+ str(22050) + 'Hz, ' +
+ str(32000) + 'Hz, ' +
+ str(44100) + 'Hz or, ' +
+ str(48000) + 'Hz.'))
return
diff --git a/mkchromecast/node.py b/mkchromecast/node.py
index e55cb821a..1c6981cbf 100644
--- a/mkchromecast/node.py
+++ b/mkchromecast/node.py
@@ -10,13 +10,12 @@
"""
import mkchromecast.__init__
-from mkchromecast.audio_devices import *
+from mkchromecast.audio_devices import inputint, outputint
import mkchromecast.colors as colors
-from mkchromecast.cast import *
-from mkchromecast.config import *
+from mkchromecast.cast import casting
+from mkchromecast.config import config_manager
import mkchromecast.messages as msg
from mkchromecast.preferences import ConfigSectionMap
-import argparse
import subprocess
import multiprocessing
import time
@@ -33,7 +32,8 @@
try:
import ConfigParser
except ImportError:
- import configparser as ConfigParser # This is for Python3
+ # This is for Python3
+ import configparser as ConfigParser
def streaming():
@@ -44,18 +44,19 @@ def streaming():
tray = mkchromecast.__init__.tray
debug = mkchromecast.__init__.debug
config = ConfigParser.RawConfigParser()
- configurations = config_manager() # Class from mkchromecast.config
+ # Class from mkchromecast.config
+ configurations = config_manager()
configf = configurations.configf
- if os.path.exists(configf) and tray == True:
+ if os.path.exists(configf) and tray is True:
configurations.chk_config()
print(colors.warning('Configuration file exists'))
print(colors.warning('Using defaults set there'))
config.read(configf)
backend = ConfigSectionMap('settings')['backend']
- rcodec= ConfigSectionMap('settings')['codec']
+ rcodec = ConfigSectionMap('settings')['codec']
bitrate = ConfigSectionMap('settings')['bitrate']
- samplerate= ConfigSectionMap('settings')['samplerate']
+ samplerate = ConfigSectionMap('settings')['samplerate']
notifications = ConfigSectionMap('settings')['notifications']
else:
backend = mkchromecast.__init__.backend
@@ -65,33 +66,38 @@ def streaming():
samplerate = str(mkchromecast.__init__.samplerate)
notifications = mkchromecast.__init__.notifications
- print(colors.options('Selected backend:')+' '+ backend)
+ print(colors.options('Selected backend:') + ' ' + backend)
- if debug == True:
- print(':::node::: variables', backend,rcodec,bitrate,samplerate,notifications)
+ if debug is True:
+ print(':::node::: variables %s, %s, %s, %s, %s' %
+ (backend, rcodec, bitrate, samplerate, notifications))
try:
youtubeurl = mkchromecast.__init__.youtubeurl
except AttributeError:
youtubeurl = None
- if youtubeurl == None:
+ if youtubeurl is None:
if backend == 'node' and rcodec != 'mp3':
- print(colors.warning('Codec '+rcodec+' is not supported by the node server!'))
- print('Using '+codec+' as default.')
+ print(colors.warning('Codec ' +
+ rcodec + ' is not supported by the node server!'))
+ print('Using ' + codec + ' as default.')
if backend == 'node':
if int(bitrate) == 192:
- print(colors.options('Default bitrate used:')+' '+bitrate+'k')
- elif int(bitrate) > 320:
- print(colors.warning('Maximum bitrate supported by '+codec+' is:')+' '+str(320)+'k')
- bitrate = '320'
+ print(colors.options('Default bitrate used:') +
+ ' ' + bitrate + 'k.')
+ elif int(bitrate) > 500:
+ print(colors.warning('Maximum bitrate supported by ' +
+ codec + ' is:') + ' ' + str(500) + 'k.')
+ bitrate = '500'
print(colors.warning('Bitrate has been set to maximum!'))
else:
- print(colors.options('Selected bitrate: ')+bitrate+'k')
+ print(colors.options('Selected bitrate: ') + bitrate + 'k.')
if samplerate == '44100':
- print(colors.options('Default sample rate used:')+' '+ samplerate+'Hz')
+ print(colors.options('Default sample rate used:') +
+ ' ' + samplerate + 'Hz.')
else:
codecs_sr = [
'mp3',
@@ -109,24 +115,30 @@ def streaming():
'ogg'
]
- if codec in codecs_sr and int(samplerate) > 22000 and int(samplerate) <= 27050:
+ if (codec in codecs_sr and int(samplerate) > 22000 and
+ int(samplerate) <= 27050):
samplerate = '22050'
msg.samplerate_no96(codec)
- if codec in codecs_sr and int(samplerate) > 27050 and int(samplerate) <= 32000:
+ if (codec in codecs_sr and int(samplerate) > 27050 and
+ int(samplerate) <= 32000):
samplerate = '32000'
msg.samplerate_no96(codec)
- elif codec in codecs_sr and int(samplerate) > 32000 and int(samplerate) <= 36000:
+ elif (codec in codecs_sr and int(samplerate) > 32000 and
+ int(samplerate) <= 36000):
samplerate = '32000'
msg.samplerate_no96(codec)
- elif codec in codecs_sr and int(samplerate) > 36000 and int(samplerate) <= 43000:
+ elif (codec in codecs_sr and int(samplerate) > 36000 and
+ int(samplerate) <= 43000):
samplerate = '44100'
msg.samplerate_no96(codec)
- print(colors.warning('Sample rate has been set to default!'))
+ print(colors.warning('Sample rate has been set to \
+ default!'))
- elif codec in codecs_sr and int(samplerate) > 43000 and int(samplerate) <= 72000:
+ elif (codec in codecs_sr and int(samplerate) > 43000 and
+ int(samplerate) <= 72000):
samplerate = '48000'
msg.samplerate_no96(codec)
@@ -134,14 +146,16 @@ def streaming():
if codec in no96k:
msg.samplerate_no96(codec)
samplerate = '48000'
- print(colors.warning('Sample rate has been set to maximum!'))
+ print(colors.warning('Sample rate has been set to \
+ maximum!'))
- print(colors.options('Sample rate set to:')+' '+samplerate+'Hz')
+ print(colors.options('Sample rate set to:') +
+ ' ' + samplerate + 'Hz.')
"""
Node section
"""
- if os.path.exists('./bin/node') == True:
+ if os.path.exists('./bin/node') is True:
webcast = [
'./bin/node',
'./nodejs/node_modules/webcast-osx-audio/bin/webcast.js',
@@ -168,24 +182,27 @@ def streaming():
'stream'
]
p = subprocess.Popen(webcast)
- if debug == True:
- print(':::node::: node command', webcast)
+ if debug is True:
+ print(':::node::: node command: %s.' % webcast)
f = open('/tmp/mkchromecast.pid', 'rb')
- pidnumber=int(pickle.load(f))
- print(colors.options('PID of main process:')+' '+str(pidnumber))
+ pidnumber = int(pickle.load(f))
+ print(colors.options('PID of main process:') + ' ' + str(pidnumber))
- localpid=getpid()
- print(colors.options('PID of streaming process:')+' '+str(localpid))
+ localpid = getpid()
+ print(colors.options('PID of streaming process:') + ' ' + str(localpid))
while p.poll() is None:
try:
time.sleep(0.5)
- if psutil.pid_exists(pidnumber) == False: # With this I ensure that if the main app fails, everything
- inputint() # will get back to normal
+ # With this I ensure that if the main app fails, everything
+ # will get back to normal
+ if psutil.pid_exists(pidnumber) is False:
+ inputint()
outputint()
parent = psutil.Process(localpid)
- for child in parent.children(recursive=True): # or parent.children() for recursive=False
+ # or parent.children() for recursive=False
+ for child in parent.children(recursive=True):
child.kill()
parent.kill()
except KeyboardInterrupt:
@@ -200,14 +217,16 @@ def streaming():
else:
print(colors.warning('Reconnecting node streaming...'))
if platform == 'Darwin' and notifications == 'enabled':
- if os.path.exists('images/google.icns') == True:
+ if os.path.exists('images/google.icns') is True:
noticon = 'images/google.icns'
else:
noticon = 'google.icns'
- if debug == True:
- print(':::node::: platform, tray, notifications', platform, tray, notifications)
+ if debug is True:
+ print(':::node::: platform, tray, notifications: %s, %s, %s.'
+ % (platform, tray, notifications))
- if platform == 'Darwin' and tray == True and notifications == 'enabled':
+ if (platform == 'Darwin' and tray is True and notifications ==
+ 'enabled'):
reconnecting = [
'./notifier/terminal-notifier.app/Contents/MacOS/terminal-notifier',
'-group',
@@ -223,11 +242,13 @@ def streaming():
]
subprocess.Popen(reconnecting)
- if debug == True:
- print(':::node::: reconnecting notifier command', reconnecting)
- relaunch(stream,recasting,kill)
+ if debug is True:
+ print(':::node::: reconnecting notifier command: %s.'
+ % reconnecting)
+ relaunch(stream, recasting, kill)
return
+
class multi_proc(object):
def __init__(self):
self.proc = multiprocessing.Process(target=streaming)
@@ -236,17 +257,20 @@ def __init__(self):
def start(self):
self.proc.start()
+
def kill():
- pid=getpid()
+ pid = getpid()
os.kill(pid, signal.SIGTERM)
return
-def relaunch(func1,func2,func3):
+
+def relaunch(func1, func2, func3):
func1()
func2()
func3()
return
+
def recasting():
start = casting()
start.initialize_cast()
@@ -254,6 +278,7 @@ def recasting():
start.play_cast()
return
+
def stream():
st = multi_proc()
st.start()
diff --git a/mkchromecast/preferences.py b/mkchromecast/preferences.py
index 26bceb4bb..bcf580e08 100644
--- a/mkchromecast/preferences.py
+++ b/mkchromecast/preferences.py
@@ -3,12 +3,11 @@
# This file is part of mkchromecast.
import sys
-import mkchromecast.__init__ # This is to verify against some needed variables
-from mkchromecast.config import *
+import mkchromecast.__init__
+from mkchromecast.config import config_manager
from mkchromecast.utils import is_installed
import os
import getpass
-import subprocess
import webbrowser
"""
@@ -21,14 +20,14 @@
USER = getpass.getuser()
if platform == 'Darwin':
- PATH ='./bin:./nodejs/bin:/Users/' + \
+ PATH = './bin:./nodejs/bin:/Users/' + \
str(USER) + \
'/bin:/usr/local/bin:/usr/local/sbin:/usr/bin:/bin:/usr/sbin:/sbin:/opt/X11/bin:/usr/X11/bin:/usr/games:' + \
os.environ['PATH']
else:
PATH = os.environ['PATH']
-if debug == True:
+if debug is True:
print('USER ='+str(USER))
print('PATH ='+str(PATH))
@@ -38,7 +37,8 @@
try:
import ConfigParser
except ImportError:
- import configparser as ConfigParser # This is for Python3
+ import configparser as ConfigParser # Python3
+
def ConfigSectionMap(section):
config = ConfigParser.RawConfigParser()
@@ -53,13 +53,13 @@ def ConfigSectionMap(section):
if dict1[option] == -1:
DebugPrint('skip: %s' % option)
except:
- print('exception on %s!' % option)
+ print('Exception on %s!' % option)
dict1[option] = None
return dict1
-if tray == True:
+if tray is True:
from PyQt5.QtWidgets import (QWidget, QLabel, QComboBox, QApplication,
- QPushButton, QLineEdit)
+ QPushButton, QLineEdit)
from PyQt5 import QtCore
class preferences(QWidget):
@@ -67,14 +67,14 @@ def __init__(self, scale_factor):
try:
super().__init__()
except TypeError:
- super(self.__class__, self).__init__() #This is to port to python2
+ super(self.__class__, self).__init__() # port to python2
self.scale_factor = scale_factor
self.config = ConfigParser.RawConfigParser()
- self.configurations = config_manager() # Class from mkchromecast.config
+ self.configurations = config_manager()
self.configf = self.configurations.configf
- if os.path.exists(self.configf) == False:
- print('config does not exist')
+ if os.path.exists(self.configf) is False:
+ print('Config file does not exist!')
self.configurations.config_defaults()
self.read_defaults()
self.initUI()
@@ -106,22 +106,24 @@ def backend(self):
self.backends = []
if platform == 'Darwin':
for item in backends_supported:
- if (is_installed(item, PATH, debug) == True
- and item != 'avconv' and item !='gstreamer'):
+ if (is_installed(item, PATH, debug) is True and
+ item != 'avconv' and item != 'gstreamer'):
self.backends.append(item)
else:
for item in backends_supported:
- if (is_installed(item, PATH, debug) == True
- and item != 'node' and item != 'gstreamer'):
+ if (is_installed(item, PATH, debug) is True and
+ item != 'node' and item != 'gstreamer'):
self.backends.append(item)
- elif (is_installed('gst-launch-1.0', PATH, debug) == True and
- item == 'gstreamer'): # hardcoded gst-launch-1.0 for gstreamer
+ # hardcoded gst-launch-1.0 for gstreamer
+ elif (is_installed('gst-launch-1.0', PATH, debug) is
+ True and item == 'gstreamer'):
self.backends.append(item)
backendindex = self.backends.index(self.backendconf)
self.backend = QLabel('Select Backend', self)
- self.backend.move(20*self.scale_factor, 24*self.scale_factor)
+ self.backend.move(20 * self.scale_factor, 24 * self.scale_factor)
self.qcbackend = QComboBox(self)
- self.qcbackend.move(180*self.scale_factor, 20*self.scale_factor)
+ self.qcbackend.move(180 * self.scale_factor,
+ 20 * self.scale_factor)
self.qcbackend.setMinimumContentsLength(7)
for item in self.backends:
self.qcbackend.addItem(item)
@@ -146,10 +148,10 @@ def codec(self):
'wav',
'flac'
]
- if debug == True:
+ if debug is True:
print(self.codecs)
codecindex = self.codecs.index(self.codecconf)
- self.qccodec.move(180*self.scale_factor, 54*self.scale_factor)
+ self.qccodec.move(180 * self.scale_factor, 54 * self.scale_factor)
self.qccodec.setMinimumContentsLength(7)
for item in self.codecs:
self.qccodec.addItem(item)
@@ -164,7 +166,8 @@ def bitrate(self):
self.bitrate.move(20 * self.scale_factor, 88 * self.scale_factor)
self.qcbitrate = QComboBox(self)
self.qcbitrate.clear()
- self.qcbitrate.move(180 * self.scale_factor, 88 * self.scale_factor)
+ self.qcbitrate.move(180 * self.scale_factor,
+ 88 * self.scale_factor)
self.qcbitrate.setMinimumContentsLength(7)
if self.codecconf == 'wav':
self.bitrates = ['None']
@@ -201,9 +204,11 @@ def samplerates(self):
]
sampleratesindex = self.samplerates.index(self.samplerateconf)
self.samplerate = QLabel('Sample Rate (Hz)', self)
- self.samplerate.move(20*self.scale_factor, 120*self.scale_factor)
+ self.samplerate.move(20 * self.scale_factor,
+ 120 * self.scale_factor)
self.qcsamplerate = QComboBox(self)
- self.qcsamplerate.move(180*self.scale_factor, 120*self.scale_factor)
+ self.qcsamplerate.move(180 * self.scale_factor,
+ 120 * self.scale_factor)
self.qcsamplerate.setMinimumContentsLength(7)
for item in self.samplerates:
self.qcsamplerate.addItem(item)
@@ -221,9 +226,11 @@ def iconcolors(self):
]
colorsindex = self.colors_list.index(self.searchcolorsconf)
self.colors = QLabel('Icon Colors', self)
- self.colors.move(20*self.scale_factor, 152*self.scale_factor)
+ self.colors.move(20 * self.scale_factor,
+ 152 * self.scale_factor)
self.qccolors = QComboBox(self)
- self.qccolors.move(180*self.scale_factor, 152*self.scale_factor)
+ self.qccolors.move(180 * self.scale_factor,
+ 152 * self.scale_factor)
self.qccolors.setMinimumContentsLength(7)
for item in self.colors_list:
self.qccolors.addItem(item)
@@ -238,11 +245,13 @@ def notifications(self):
'enabled',
'disabled'
]
- notindex = self.notifications_list.index(self.notificationsconf)
+ notindex = self.notifications_list.index(self.notifconf)
self.notifications = QLabel('Notifications', self)
- self.notifications.move(20*self.scale_factor, 184*self.scale_factor)
+ self.notifications.move(20 * self.scale_factor,
+ 184 * self.scale_factor)
self.qcnotifications = QComboBox(self)
- self.qcnotifications.move(180*self.scale_factor, 184*self.scale_factor)
+ self.qcnotifications.move(180 * self.scale_factor,
+ 184 * self.scale_factor)
self.qcnotifications.setMinimumContentsLength(7)
for item in self.notifications_list:
self.qcnotifications.addItem(item)
@@ -257,11 +266,13 @@ def searchatlaunch(self):
'enabled',
'disabled'
]
- launchindex = self.atlaunch_list.index(self.searchatlaunchconf)
+ launchindex = self.atlaunch_list.index(self.satlaunchconf)
self.atlaunch = QLabel('Search At Launch', self)
- self.atlaunch.move(20*self.scale_factor, 214*self.scale_factor)
+ self.atlaunch.move(20 * self.scale_factor,
+ 214 * self.scale_factor)
self.qcatlaunch = QComboBox(self)
- self.qcatlaunch.move(180*self.scale_factor, 214*self.scale_factor)
+ self.qcatlaunch.move(180 * self.scale_factor,
+ 214 * self.scale_factor)
self.qcatlaunch.setMinimumContentsLength(7)
for item in self.atlaunch_list:
self.qcatlaunch.addItem(item)
@@ -273,12 +284,14 @@ def alsadevice(self):
Set the ALSA Device
"""
self.alsadevice = QLabel('ALSA Device', self)
- self.alsadevice.move(20*self.scale_factor, 244*self.scale_factor)
+ self.alsadevice.move(20 * self.scale_factor,
+ 244 * self.scale_factor)
self.qle = QLineEdit(self)
- self.qle.move(179*self.scale_factor, 244*self.scale_factor)
+ self.qle.move(179 * self.scale_factor,
+ 244 * self.scale_factor)
self.qle.setFixedWidth(84*self.scale_factor)
self.read_defaults()
- if self.alsadeviceconf != None:
+ if self.alsadeviceconf is not None:
self.qle.setText(self.alsadeviceconf)
self.qle.textChanged[str].connect(self.onActivatedalsadevice)
@@ -287,27 +300,36 @@ def buttons(self):
Buttons
"""
resetbtn = QPushButton("Reset Settings", self)
- resetbtn.move(10*self.scale_factor, 274*self.scale_factor)
+ resetbtn.move(10 * self.scale_factor, 274 * self.scale_factor)
resetbtn.clicked.connect(self.reset_configuration)
faqbtn = QPushButton("FAQ", self)
- faqbtn.move(138*self.scale_factor, 274*self.scale_factor)
+ faqbtn.move(138 * self.scale_factor, 274 * self.scale_factor)
faqbtn.clicked.connect(lambda: webbrowser.open('https://github.com/muammar/mkchromecast/wiki/FAQ'))
donbtn = QPushButton("Donate :)", self)
- donbtn.move(204*self.scale_factor, 274*self.scale_factor)
+ donbtn.move(204 * self.scale_factor, 274 * self.scale_factor)
donbtn.clicked.connect(lambda: webbrowser.open('https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=RZLF7TDCAXT9Q&lc=US&item_name=mkchromecast¤cy_code=USD&bn=PP%2dDonationsBF%3abtn_donateCC_LG%2egif%3aNonHosted'))
def window(self):
"""
Geometry and window's title
"""
- self.setGeometry(300*self.scale_factor, 300*self.scale_factor, 300*self.scale_factor, 200*self.scale_factor)
+ self.setGeometry(300 * self.scale_factor,
+ 300 * self.scale_factor,
+ 300 * self.scale_factor,
+ 200 * self.scale_factor)
if platform == 'Darwin':
- self.setFixedSize(310*self.scale_factor, 320*self.scale_factor) #This is to fix the size of the window
+ # This is to fix the size of the window
+ self.setFixedSize(310 * self.scale_factor,
+ 320 * self.scale_factor)
else:
- self.setFixedSize(282*self.scale_factor, 320*self.scale_factor) #This is to fix the size of the window
- self.setWindowFlags(QtCore.Qt.WindowCloseButtonHint | QtCore.Qt.WindowMinimizeButtonHint | QtCore.Qt.WindowStaysOnTopHint)
+ # This is to fix the size of the window
+ self.setFixedSize(282 * self.scale_factor,
+ 320 * self.scale_factor)
+ self.setWindowFlags(QtCore.Qt.WindowCloseButtonHint |
+ QtCore.Qt.WindowMinimizeButtonHint |
+ QtCore.Qt.WindowStaysOnTopHint)
self.setWindowTitle('Mkchromecast Preferences')
"""
@@ -339,8 +361,8 @@ def reset_indexes(self):
self.qcbitrate.addItem(item)
sampleratesindex = self.samplerates.index(self.samplerateconf)
colorsindex = self.colors_list.index(self.searchcolorsconf)
- notindex = self.notifications_list.index(self.notificationsconf)
- launchindex = self.atlaunch_list.index(self.searchatlaunchconf)
+ notindex = self.notifications_list.index(self.notifconf)
+ launchindex = self.atlaunch_list.index(self.satlaunchconf)
self.qcbackend.setCurrentIndex(backendindex)
self.qccodec.setCurrentIndex(codecindex)
self.qcbitrate.setCurrentIndex(bitrateindex)
@@ -368,10 +390,10 @@ def onActivatedbk(self, text):
'wav',
'flac'
]
- if debug == True:
- print(codecs)
+ if debug is True:
+ print('Codecs: %s.' % codecs)
codecindex = codecs.index(self.codecconf)
- self.qccodec.move(180*self.scale_factor, 54*self.scale_factor)
+ self.qccodec.move(180 * self.scale_factor, 54 * self.scale_factor)
self.qccodec.setMinimumContentsLength(7)
for item in codecs:
self.qccodec.addItem(item)
@@ -400,7 +422,8 @@ def onActivatedcc(self, text):
'500'
]
bitrateindex = bitrates.index(self.bitrateconf)
- self.qcbitrate.move(180*self.scale_factor, 88*self.scale_factor)
+ self.qcbitrate.move(180 * self.scale_factor,
+ 88 * self.scale_factor)
for item in bitrates:
self.qcbitrate.addItem(item)
self.qcbitrate.setCurrentIndex(bitrateindex)
@@ -455,15 +478,15 @@ def read_defaults(self):
self.codecconf = ConfigSectionMap('settings')['codec']
self.bitrateconf = ConfigSectionMap('settings')['bitrate']
self.samplerateconf = ConfigSectionMap('settings')['samplerate']
- self.notificationsconf = ConfigSectionMap('settings')['notifications']
+ self.notifconf = ConfigSectionMap('settings')['notifications']
self.searchcolorsconf = ConfigSectionMap('settings')['colors']
- self.searchatlaunchconf = ConfigSectionMap('settings')['searchatlaunch']
+ self.satlaunchconf = ConfigSectionMap('settings')['searchatlaunch']
self.alsadeviceconf = ConfigSectionMap('settings')['alsadevice']
- if debug == True:
- print(self.backendconf, self.codecconf, self.bitrateconf, \
- self.samplerateconf, self.notificationsconf, \
- self.searchatlaunchconf, self.searchcolorsconf, \
- self.alsadeviceconf)
+ if debug is True:
+ print(self.backendconf, self.codecconf, self.bitrateconf,
+ self.samplerateconf, self.notifconf,
+ self.satlaunchconf, self.searchcolorsconf,
+ self.alsadeviceconf)
def write_config(self):
"""This method writes to configfile"""
diff --git a/mkchromecast/pulseaudio.py b/mkchromecast/pulseaudio.py
index 6fc875d20..7b37d35bb 100644
--- a/mkchromecast/pulseaudio.py
+++ b/mkchromecast/pulseaudio.py
@@ -5,21 +5,22 @@
import subprocess
import time
+
def create_sink():
- sink_name = 'mkchromecast'
+ sink_name = 'Mkchromecast'
create_sink = [
'pactl',
'load-module',
'module-null-sink',
- 'sink_name='+sink_name
+ 'sink_name=' + sink_name
]
rename_sink = [
'pacmd',
'update-sink-proplist',
sink_name,
- 'device.description='+sink_name
+ 'device.description=' + sink_name
]
cs = subprocess.Popen(
@@ -39,6 +40,7 @@ def create_sink():
rsoutput, rserror = rs.communicate()
return
+
def remove_sink():
remove_sink = [
'pactl',
@@ -54,6 +56,7 @@ def remove_sink():
rmsoutput, rmserror = rms.communicate()
return
+
def check_sink():
check_sink = [
'pacmd',
@@ -67,12 +70,12 @@ def check_sink():
chkoutput, chkerror = chk.communicate()
try:
- if 'mkchromecast' in chkoutput:
+ if 'Mkchromecast' in chkoutput:
return True
else:
return False
except TypeError:
- if 'mkchromecast' in chkoutput.decode('utf-8'):
+ if 'Mkchromecast' in chkoutput.decode('utf-8'):
return True
else:
return False
diff --git a/mkchromecast/resolution.py b/mkchromecast/resolution.py
new file mode 100644
index 000000000..fb01c9280
--- /dev/null
+++ b/mkchromecast/resolution.py
@@ -0,0 +1,21 @@
+#!/usr/bin/env python
+
+# This file is part of mkchromecast.
+
+resolutions = {
+ '480p': ('scale-854:-1', '854x480:'),
+ '720p': ('scale=1280:-1', '1280x720'),
+ '1366x768': ('scale=1366x768', '1366x768'),
+ '1080p': ('scale=1920x1080', '1920x1080'),
+ '2k': ('scale=2048x1148', '2048x1148'),
+ 'uhd': ('scale=3840x2160', '3840x2160'),
+ '4k': ('scale=4096:-1', '4096x2160')
+ }
+
+
+def resolution(res, screencast):
+ res = resolutions[res.lower()]
+ if not screencast:
+ return ['-vf', res[0]]
+ else:
+ return res[1]
diff --git a/mkchromecast/systray.py b/mkchromecast/systray.py
index 483456bb8..72d14e7d2 100644
--- a/mkchromecast/systray.py
+++ b/mkchromecast/systray.py
@@ -4,36 +4,36 @@
# brew install pyqt5 --with-python --without-python3
from __future__ import division
-import mkchromecast.__init__ # This is to verify against some needed variables
-from mkchromecast.audio_devices import *
-from mkchromecast.cast import *
-from mkchromecast.config import *
+import mkchromecast.__init__
+from mkchromecast.audio_devices import inputint, outputint
+from mkchromecast.cast import casting
+from mkchromecast.config import config_manager
from mkchromecast.preferences import ConfigSectionMap
-from mkchromecast.node import *
import mkchromecast.preferences
-from mkchromecast.pulseaudio import *
+import mkchromecast.colors as colors
+from mkchromecast.pulseaudio import remove_sink
+from mkchromecast.utils import del_tmp
import mkchromecast.tray_threading
from PyQt5 import QtCore, QtGui, QtWidgets
-from PyQt5.QtCore import (QThread, QObject, pyqtSignal, pyqtSlot, Qt)
-from PyQt5.QtWidgets import (QWidget, QSlider, QLabel, QApplication,
- QMessageBox, QMainWindow)
-from PyQt5.QtGui import QPixmap
+from PyQt5.QtCore import QThread, Qt
+from PyQt5.QtWidgets import QWidget, QMessageBox
import pychromecast
+import socket
from pychromecast.dial import reboot
import signal
import os.path
import psutil
import pickle
import subprocess
-import threading
from os import getpid
+import sys
"""
Configparser is imported differently in Python3
"""
try:
import ConfigParser
except ImportError:
- import configparser as ConfigParser # This is for Python3
+ import configparser as ConfigParser
"""
urllib is imported differently in Python3
@@ -46,6 +46,7 @@
platform = mkchromecast.__init__.platform
debug = mkchromecast.__init__.debug
+
class menubar(QtWidgets.QMainWindow):
def __init__(self):
self.cc = casting()
@@ -57,7 +58,7 @@ def __init__(self):
self.read_config()
"""
- These dictionaries are used to set the icons' colors
+ These dictionaries are used to set icons' colors
"""
self.google = {
'black': 'google',
@@ -120,44 +121,46 @@ def __init__(self):
else:
self.scale_factor = 1
- if debug == True:
+ if debug is True:
print(':::systray::: Screen resolution: ', self.width, self.height)
- self.app.setQuitOnLastWindowClosed(False) # This avoid the QMessageBox to close parent processes.
+ # This avoid the QMessageBox to close parent processes.
+ self.app.setQuitOnLastWindowClosed(False)
if hasattr(QtCore.Qt, 'AA_UseHighDpiPixmaps'):
self.app.setAttribute(QtCore.Qt.AA_UseHighDpiPixmaps)
- if debug == True:
+ if debug is True:
print(':::systray::: High-DPI screen detected...')
self.w = QWidget()
- if os.path.exists('images/'+self.google[self.colors]+'.icns') == True: # This is useful when launching from git repo
+ # This is useful when launching from git repo
+ if os.path.exists('images/' +
+ self.google[self.colors] + '.icns') is True:
self.icon = QtGui.QIcon()
if platform == 'Darwin':
self.icon.addFile(
- 'images/'
- + self.google[self.colors]
- + '.icns'
+ 'images/' +
+ self.google[self.colors] +
+ '.icns'
)
else:
self.icon.addFile(
- 'images/'
- + self.google[self.colors]
- + '.png'
+ 'images/' +
+ self.google[self.colors] +
+ '.png'
)
- else: # This is useful for applications
+ else:
self.icon = QtGui.QIcon()
if platform == 'Linux':
self.icon.addFile(
- '/usr/share/mkchromecast/images/'
- + self.google[self.colors]
- + '.png'
+ '/usr/share/mkchromecast/images/' +
+ self.google[self.colors] +
+ '.png'
)
else:
- self.icon.addFile(
- self.google[self.colors]
- + '.icns')
- super(QtWidgets.QMainWindow,self).__init__()
+ self.icon.addFile(self.google[self.colors] +
+ '.icns')
+ super(QtWidgets.QMainWindow, self).__init__()
self.createUI()
def createUI(self):
@@ -184,7 +187,7 @@ def createUI(self):
"""
if self.searchatlaunch == 'enabled':
self.search_cast()
- self.app.exec_() #We start showing the system tray
+ self.app.exec_() # We start showing the system tray
def read_config(self):
"""
@@ -199,19 +202,21 @@ def read_config(self):
print(colors.warning('Using defaults set there'))
config.read(configf)
self.notifications = ConfigSectionMap('settings')['notifications']
- self.searchatlaunch = ConfigSectionMap('settings')['searchatlaunch']
+ self.searchatlaunch = ConfigSectionMap('settings')[
+ 'searchatlaunch']
self.colors = ConfigSectionMap('settings')['colors']
else:
self.notifications = 'disabled'
self.searchatlaunch = 'disabled'
- self.colors= 'black'
- if debug == True:
+ self.colors = 'black'
+ if debug is True:
print(':::systray::: self.notifications '+self.notifications)
print(':::systray::: self.searchatlaunch '+self.searchatlaunch)
print(':::systray::: self.colors '+self.colors)
def search_menu(self):
- self.SearchAction = self.menu.addAction('Search For Media Streaming Devices')
+ self.SearchAction = self.menu.addAction('Search For Media '
+ 'Streaming Devices')
self.SearchAction.triggered.connect(self.search_cast)
def stop_menu(self):
@@ -226,7 +231,7 @@ def separator_menu(self):
self.menu.addSeparator()
def populating_menu(self):
- if self.SearchAction.triggered.connect == True:
+ if self.SearchAction.triggered.connect is True:
self.cast_list()
def resetaudio_menu(self):
@@ -262,132 +267,147 @@ def onIntReady(self, availablecc):
self.availablecc = availablecc
self.cast_list()
- def search_cast(self):
- self.read_config()
+ def set_icon_working(self):
+ """docstring for fnamicon_working"""
if self.notifications == 'enabled':
self.search_notification()
- if os.path.exists('images/'+self.google_working[self.colors]+'.icns') == True:
+ if os.path.exists('images/' +
+ self.google_working[self.colors]+'.icns') is True:
if platform == 'Darwin':
self.tray.setIcon(
QtGui.QIcon(
- 'images/'
- + self.google_working[self.colors]
- + '.icns'
+ 'images/' +
+ self.google_working[self.colors] +
+ '.icns'
)
)
else:
self.tray.setIcon(
QtGui.QIcon(
- 'images/'
- + self.google_working[self.colors]
- + '.png'
+ 'images/' +
+ self.google_working[self.colors] +
+ '.png'
)
)
else:
if platform == 'Linux':
self.tray.setIcon(
QtGui.QIcon(
- '/usr/share/mkchromecast/images/'
- + self.google_working[self.colors]
- + '.png'
+ '/usr/share/mkchromecast/images/' +
+ self.google_working[self.colors] +
+ '.png'
)
)
else:
self.tray.setIcon(
QtGui.QIcon(
- self.google_working[self.colors]
- + '.icns'
+ self.google_working[self.colors] +
+ '.icns'
)
)
- """
- This catches the error caused by an empty .tmp file
- """
- if os.path.exists('/tmp/mkchromecast.tmp') == True:
- try:
- self.tf = open('/tmp/mkchromecast.tmp', 'rb')
- self.index=pickle.load(self.tf)
- except EOFError:
- os.remove('/tmp/mkchromecast.tmp')
-
- if self.stopped == True and os.path.exists('/tmp/mkchromecast.tmp') == True:
- os.remove('/tmp/mkchromecast.tmp')
-
- self.thread.start()
+ def set_icon_idle(self):
+ """docstring for icon_idle"""
+ if os.path.exists('images/'+self.google[self.colors]+'.icns') is True:
+ if platform == 'Darwin':
+ self.tray.setIcon(
+ QtGui.QIcon(
+ 'images/' +
+ self.google[self.colors] +
+ '.icns'
+ )
+ )
+ else:
+ self.tray.setIcon(
+ QtGui.QIcon(
+ 'images/' +
+ self.google[self.colors] +
+ '.png'
+ )
+ )
+ else:
+ if platform == 'Linux':
+ self.tray.setIcon(
+ QtGui.QIcon(
+ '/usr/share/mkchromecast/images/' +
+ self.google[self.colors] +
+ '.png'
+ )
+ )
+ else:
+ self.tray.setIcon(
+ QtGui.QIcon(
+ self.google[self.colors] +
+ '.icns'
+ )
+ )
- def cast_list(self):
- if os.path.exists('images/'+self.google[self.colors]+'.icns') == True:
+ def set_icon_nodev(self):
+ """docstring for set_ic"""
+ if os.path.exists('images/' +
+ self.google_nodev[self.colors]+'.icns') is True:
if platform == 'Darwin':
self.tray.setIcon(
QtGui.QIcon(
- 'images/'
- + self.google[self.colors]
- + '.icns'
+ 'images/' +
+ self.google_nodev[self.colors] +
+ '.icns'
)
)
else:
self.tray.setIcon(
QtGui.QIcon(
- 'images/'
- + self.google[self.colors]
- + '.png'
+ 'images/' +
+ self.google_nodev[self.colors] +
+ '.png'
)
)
else:
if platform == 'Linux':
self.tray.setIcon(
QtGui.QIcon(
- '/usr/share/mkchromecast/images/'
- + self.google[self.colors]
- + '.png'
+ '/usr/share/mkchromecast/images/' +
+ self.google_nodev[self.colors] +
+ '.png'
)
)
else:
self.tray.setIcon(
QtGui.QIcon(
- self.google[self.colors]
- + '.icns'
+ self.google_nodev[self.colors] +
+ '.icns'
)
)
+ def search_cast(self):
+ self.read_config()
+ self.set_icon_working()
+ """
+ This catches the error caused by an empty .tmp file
+ """
+ if os.path.exists('/tmp/mkchromecast.tmp') is True:
+ try:
+ self.tf = open('/tmp/mkchromecast.tmp', 'rb')
+ self.index = pickle.load(self.tf)
+ except EOFError:
+ os.remove('/tmp/mkchromecast.tmp')
+
+ if (self.stopped is True and
+ os.path.exists('/tmp/mkchromecast.tmp') is True):
+ os.remove('/tmp/mkchromecast.tmp')
+
+ self.thread.start()
+
+ def cast_list(self):
+ self.set_icon_idle()
+
if len(self.availablecc) == 0:
self.menu.clear()
self.search_menu()
self.separator_menu()
- self.NodevAction = self.menu.addAction('No Streaming Devices Found.')
- if os.path.exists('images/'+self.google_nodev[self.colors]+'.icns') == True:
- if platform == 'Darwin':
- self.tray.setIcon(
- QtGui.QIcon(
- 'images/'
- + self.google_nodev[self.colors]
- + '.icns'
- )
- )
- else:
- self.tray.setIcon(
- QtGui.QIcon(
- 'images/'
- + self.google_nodev[self.colors]
- + '.png'
- )
- )
- else:
- if platform == 'Linux':
- self.tray.setIcon(
- QtGui.QIcon(
- '/usr/share/mkchromecast/images/'
- + self.google_nodev[self.colors]
- + '.png'
- )
- )
- else:
- self.tray.setIcon(
- QtGui.QIcon(
- self.google_nodev[self.colors]
- +'.icns'
- )
- )
+ self.NodevAction = self.menu.addAction(
+ 'No Streaming Devices Found.')
+ self.set_icon_nodev()
self.separator_menu()
self.stop_menu()
@@ -402,7 +422,8 @@ def cast_list(self):
else:
self.read_config()
if platform == 'Darwin' and self.notifications == 'enabled':
- if os.path.exists('images/'+self.google[self.colors]+'.icns') == True:
+ if os.path.exists('images/' +
+ self.google[self.colors]+'.icns') is True:
noticon = 'images/'+self.google[self.colors]+'.icns'
else:
noticon = self.google[self.colors]+'.icns'
@@ -419,32 +440,36 @@ def cast_list(self):
'Media Streaming Devices Found!'
]
subprocess.Popen(found)
- if debug == True:
- print(':::systray:::',found)
+ if debug is True:
+ print(':::systray:::', found)
elif platform == 'Linux' and self.notifications == 'enabled':
try:
import gi
gi.require_version('Notify', '0.7')
from gi.repository import Notify
Notify.init('Mkchromecast')
- found=Notify.Notification.new(
+ found = Notify.Notification.new(
'Mkchromecast',
'Media Streaming Devices Found!',
'dialog-information'
)
found.show()
except ImportError:
- print('If you want to receive notifications in Linux, install libnotify and python-gobject')
+ print('If you want to receive notifications in Linux, '
+ 'install libnotify and python-gobject')
self.menu.clear()
self.search_menu()
self.separator_menu()
print('Available Media Streaming Devices', self.availablecc)
for index, menuentry in enumerate(self.availablecc):
try:
- self.a = self.ag.addAction((QtWidgets.QAction(str(menuentry[1]), self, checkable=True)))
+ self.a = self.ag.addAction(
+ (QtWidgets.QAction(
+ str(menuentry[1]), self, checkable=True)))
self.menuentry = self.menu.addAction(self.a)
except UnicodeEncodeError:
- self.menuentry = self.menu.addAction(str(unicode(menuentry[1]).encode("utf-8")))
+ self.menuentry = self.menu.addAction(str(
+ unicode(menuentry[1]).encode("utf-8")))
# The receiver is a lambda function that passes clicked as
# a boolean, and the clicked_item as an argument to the
# self.clicked_cc() method. This last method, sets the correct
@@ -466,14 +491,14 @@ def cast_list(self):
self.exit_menu()
def clicked_cc(self, clicked_item):
- if self.played == True:
+ if self.played is True:
try:
self.cast.quit_app()
except AttributeError:
self.cast.stop()
- if debug == True:
- print(clicked_item)
+ if debug is True:
+ print(':::tray::: clicked item: %s.' % clicked_item)
self.index = clicked_item[0]
self.cast_to = clicked_item[1]
self.play_cast()
@@ -482,120 +507,27 @@ def pcastready(self, message):
print('pcastready ?', message)
if message == '_play_cast_ success':
self.pcastfailed = False
- if os.path.exists('/tmp/mkchromecast.tmp') == True:
+ if os.path.exists('/tmp/mkchromecast.tmp') is True:
self.cast = mkchromecast.tray_threading.cast
- if os.path.exists('images/'+self.google[self.colors]+'.icns') == True:
- if platform == 'Darwin':
- self.tray.setIcon(
- QtGui.QIcon(
- 'images/'
- + self.google[self.colors]
- + '.icns'
- )
- )
- else:
- self.tray.setIcon(
- QtGui.QIcon(
- 'images/'
- + self.google[self.colors]
- + '.png'
- )
- )
- else:
- if platform == 'Linux':
- self.tray.setIcon(
- QtGui.QIcon(
- '/usr/share/mkchromecast/images/'
- + self.google[self.colors]
- + '.png'
- )
- )
- else:
- self.tray.setIcon(
- QtGui.QIcon(
- self.google[self.colors]
- +'.icns'
- )
- )
+ self.set_icon_idle()
else:
self.pcastfailed = True
- if os.path.exists('images/'+self.google_nodev[self.colors]+'.icns') == True:
- if platform == 'Darwin':
- self.tray.setIcon(
- QtGui.QIcon(
- 'images/'
- + self.google_nodev[self.colors]
- + '.icns'
- )
- )
- else:
- self.tray.setIcon(
- QtGui.QIcon(
- 'images/'
- + self.google_nodev[self.colors]
- + '.png'
- )
- )
- else:
- if platform == 'Linux':
- self.tray.setIcon(
- QtGui.QIcon(
- '/usr/share/mkchromecast/images/'
- + self.google_nodev[self.colors]
- + '.png'
- )
- )
- else:
- self.tray.setIcon(
- QtGui.QIcon(
- self.google_nodev[self.colors]
- + '.icns'
- )
- )
+ self.set_icon_nodev()
self.stop_cast()
- pass # This should stop the play process when there is an error in the threading _play_cast_
+ # This should stop the play process when there is an error in the
+ # threading _play_cast_
+ pass
def play_cast(self):
- if self.played == True:
+ if self.played is True:
self.kill_child()
- if os.path.exists('images/'+self.google_working[self.colors]+'.icns') == True:
- if platform == 'Darwin':
- self.tray.setIcon(
- QtGui.QIcon(
- 'images/'
- + self.google_working[self.colors]
- + '.icns'
- )
- )
- else:
- self.tray.setIcon(
- QtGui.QIcon(
- 'images/'
- + self.google_working[self.colors]
- + '.png'
- )
- )
- else:
- if platform == 'Linux':
- self.tray.setIcon(
- QtGui.QIcon(
- '/usr/share/mkchromecast/images/'
- + self.google_working[self.colors]
- + '.png'
- )
- )
- else:
- self.tray.setIcon(
- QtGui.QIcon(
- self.google_working[self.colors]
- + '.icns'
- )
- )
+
+ self.set_icon_working()
while True:
try:
- if os.path.exists('/tmp/mkchromecast.tmp') == True:
+ if os.path.exists('/tmp/mkchromecast.tmp') is True:
self.tf = open('/tmp/mkchromecast.tmp', 'wb')
pickle.dump(self.cast_to, self.tf)
self.tf.close()
@@ -606,38 +538,44 @@ def play_cast(self):
self.threadplay.start()
def stop_cast(self):
- if self.stopped == False:
+ if self.stopped is False:
pass
- if self.cast != None or self.stopped == True or self.pcastfailed == True:
-
+ if (self.cast is not None or self.stopped is True or self.pcastfailed
+ is True):
try:
self.cast.quit_app()
except AttributeError:
- self.cast.stop() # This is for sonos. The thing is that if we are at this point, user requested an stop or cast failed.
+ # This is for sonos. The thing is that if we are at this point,
+ # user requested an stop or cast failed.
+ self.cast.stop()
self.reset_audio()
try:
self.kill_child()
except psutil.NoSuchProcess:
pass
- checkmktmp()
+ mkchromecast.__init__.checkmktmp()
self.search_cast()
- while True: # This is to retry when stopping and pychromecast.error.NotConnected raises.
+ # This is to retry when stopping and
+ # pychromecast.error.NotConnected raises.
+ while True:
try:
self.cast.quit_app()
except pychromecast.error.NotConnected:
continue
except AttributeError:
- self.cast.stop() # This is for sonos. The thing is that if we are at this point, user requested an stop or cast failed.
+ # This is for sonos. The thing is that if we are at this
+ # point, user requested an stop or cast failed.
+ self.cast.stop()
break
self.stopped = True
self.read_config()
if platform == 'Darwin' and self.notifications == 'enabled':
- if self.pcastfailed == True:
+ if self.pcastfailed is True:
stop = [
'./notifier/terminal-notifier.app/Contents/MacOS/terminal-notifier',
'-group',
@@ -658,7 +596,7 @@ def stop_cast(self):
'Streaming Stopped!'
]
subprocess.Popen(stop)
- if debug == True:
+ if debug is True:
print(':::systray::: stop', stop)
elif platform == 'Linux' and self.notifications == 'enabled':
@@ -667,21 +605,22 @@ def stop_cast(self):
gi.require_version('Notify', '0.7')
from gi.repository import Notify
Notify.init('Mkchromecast')
- if self.pcastfailed == True:
- stop=Notify.Notification.new(
+ if self.pcastfailed is True:
+ stop = Notify.Notification.new(
'Mkchromecast',
'Streaming Process Failed. Try Again...',
'dialog-information'
)
else:
- stop=Notify.Notification.new(
+ stop = Notify.Notification.new(
'Mkchromecast',
'Streaming Stopped!',
'dialog-information'
)
stop.show()
except ImportError:
- print('If you want to receive notifications in Linux, install libnotify and python-gobject')
+ print('If you want to receive notifications in Linux, '
+ 'install libnotify and python-gobject')
def volume_cast(self):
self.sl = QtWidgets.QSlider(Qt.Horizontal)
@@ -689,18 +628,19 @@ def volume_cast(self):
self.sl.setGeometry(
30 * self.scale_factor,
40 * self.scale_factor,
- 260 * self.scale_factor,
+ 260 * self.scale_factor,
70 * self.scale_factor
)
self.sl.setWindowFlags(QtCore.Qt.WindowStaysOnTopHint)
try:
- self.maxvolset = 40
+ self.maxvolset = 100
self.sl.setMaximum(self.maxvolset)
- self.sl.setValue(round((self.cast.status.volume_level*self.maxvolset), 1))
+ self.sl.setValue(round((self.cast.status.volume_level*self.maxvolset),
+ 1))
except AttributeError:
self.maxvolset = 100
self.sl.setMaximum(self.maxvolset)
- if self.played == False:
+ if self.played is False:
self.sl.setValue(2)
else:
try:
@@ -713,32 +653,37 @@ def volume_cast(self):
def value_changed(self, value):
try:
+ """
+ Chromecast volume
+ """
+ volume = value/self.maxvolset
+ self.cast.set_volume(volume)
if round(self.cast.status.volume_level, 1) == 1:
- print (colors.warning(':::systray::: Maximum volume level reached!'))
- volume = value/self.maxvolset
- self.cast.set_volume(volume)
- else:
- volume = value/self.maxvolset
- self.cast.set_volume(volume)
- if debug == True:
+ print(colors.warning(
+ ':::systray::: Maximum volume level reached!'))
+
+ if debug is True:
print(':::systray::: Volume set to: '+str(volume))
except AttributeError:
+ pass
+
+ try:
"""
Sonos volume
"""
self.maxvolset = 100
+ volume = value
+ self.cast.volume = volume
+ self.cast.play()
if (self.cast.volume) == 100:
- print (colors.warning(':::systray::: Maximum volume level reached!'))
- volume = value
- self.cast.volume = volume
- self.cast.play()
- else:
- volume = value
- self.cast.volume = volume
- self.cast.play()
- if debug == True:
+ print(colors.warning(':::systray::: Maximum volume reached!'))
+
+ if debug is True:
print(':::systray::: Volume set to: '+str(volume))
- if debug == True:
+ except AttributeError:
+ pass
+
+ if debug is True:
print(':::systray::: Volume changed: '+str(value))
def reset_audio(self):
@@ -751,7 +696,7 @@ def reset_audio(self):
def reboot(self):
if platform == 'Darwin':
try:
- self.cast.host_ = socket.gethostbyname(self.cast_to+'.local')
+ self.cast.host_ = socket.gethostbyname(self.cast_to + '.local')
print('Cast device IP: '+str(self.cast.host_))
self.reset_audio()
self.stop_cast()
@@ -762,7 +707,8 @@ def reboot(self):
self.stop_cast()
reboot(self.cast.host)
except AttributeError:
- pass # I should add a notification here
+ # FIXME I should add a notification here
+ pass
else:
try:
print('Cast device IP: %s' % str(self.cast.host))
@@ -772,13 +718,15 @@ def reboot(self):
except AttributeError:
self.reset_audio()
self.stop_cast()
- for device in self.availablecc:
- if self.cast_to in device:
- ip = device[3]
- print('Cast device IP: %s' % str(ip))
- url = 'http://' + ip + ':1400/reboot'
- urlopen(url).read()
-
+ try:
+ for device in self.availablecc:
+ if self.cast_to in device:
+ ip = device[3]
+ print('Sonos device IP: %s' % str(ip))
+ url = 'http://' + ip + ':1400/reboot'
+ urlopen(url).read()
+ except AttributeError:
+ pass
def preferences_show(self):
self.p = mkchromecast.preferences.preferences(self.scale_factor)
@@ -789,16 +737,19 @@ def updateready(self, message):
updaterBox = QMessageBox()
updaterBox.setWindowFlags(QtCore.Qt.WindowStaysOnTopHint)
updaterBox.setIcon(QMessageBox.Information)
- updaterBox.setTextFormat(Qt.RichText) # This option let you write rich text in pyqt5.
+ # This option let you write rich text in pyqt5.
+ updaterBox.setTextFormat(Qt.RichText)
if message == 'None':
updaterBox.setText('No network connection detected!')
- updaterBox.setInformativeText("""Verify that your computer is connected to your router, and try again.""")
+ updaterBox.setInformativeText("""
+ Verify that your computer is connected to your router,
+ and try again.""")
elif message == 'False':
updaterBox.setText('Your installation is up-to-date!')
updaterBox.setInformativeText(
- 'Mkchromecast v'
- + mkchromecast.__init__.__version__
- + ' is currently the newest version available.'
+ 'Mkchromecast v' +
+ mkchromecast.__init__.__version__ +
+ ' is currently the newest version available.'
)
elif message == 'error1':
updaterBox.setText('Problems connecting to remote file server!')
@@ -806,21 +757,21 @@ def updateready(self, message):
else:
updaterBox.setText('New version of Mkchromecast available!')
if platform == 'Darwin':
- downloadurl = (
- ''
+ download = (
+ ''
)
elif platform == 'Linux':
- downloadurl = ''
- if debug == True:
- print('Download URL:', downloadurl)
+ download = ''
+ if debug is True:
+ print('Download URL:', download)
updaterBox.setInformativeText(
- 'You can '
- + downloadurl
- + 'download it by clicking here.'
+ 'You can ' +
+ download +
+ 'download it by clicking here.'
)
updaterBox.setStandardButtons(QMessageBox.Ok)
updaterBox.exec_()
@@ -831,22 +782,25 @@ def update_show(self):
def about_show(self):
msgBox = QMessageBox()
msgBox.setWindowFlags(QtCore.Qt.WindowStaysOnTopHint)
- if os.path.exists('images/google.icns') == True: # This is useful when launching from git repo
- if platform == 'Darwin':
- self.abouticon='images/google.icns'
+ # This is useful when launching from git repo
+ if os.path.exists('images/google.icns') is True:
+ if platform is 'Darwin':
+ self.about_icon = 'images/google.icns'
else:
- self.abouticon='images/google.png'
- else: # This is useful for applications
- if platform == 'Linux':
- self.abouticon='/usr/share/mkchromecast/images/google.png'
+ self.about_icon = 'images/google.png'
+ # This is useful for applications
+ else:
+ if platform is 'Linux':
+ self.about_icon = '/usr/share/mkchromecast/images/google.png'
else:
- self.abouticon='google.icns'
+ self.about_icon = 'google.icns'
msgsettext = (
- '
Mkchromecast v'
- + mkchromecast.__init__.__version__
+ '
' +
+ ' Mkchromecast v' +
+ mkchromecast.__init__.__version__
)
msgBox.setText(msgsettext)
msgBox.setInformativeText("""
@@ -867,7 +821,10 @@ def about_show(self):
This program comes with absolutely no warranty.
- See the MIT license for details.
+ See the
+
+ MIT license for details.
""")
msgBox.exec_()
@@ -875,13 +832,15 @@ def about_show(self):
def kill_child(self): # Not a beautiful name, I know...
self.parent_pid = getpid()
self.parent = psutil.Process(self.parent_pid)
- for child in self.parent.children(recursive=True): # or parent.children() for recursive=False
+ # or parent.children() for recursive=False
+ for child in self.parent.children(recursive=True):
child.kill()
def exit_all(self):
- if self.cast == None and self.stopped == False:
+ del_tmp()
+ if self.cast is None and self.stopped is False:
self.app.quit()
- elif self.stopped == True or self.cast != None:
+ elif self.stopped is True or self.cast is not None:
self.kill_child()
self.stop_cast()
self.app.quit()
@@ -892,17 +851,19 @@ def exit_all(self):
Notifications
"""
def search_notification(self):
- if platform == 'Darwin' and self.notifications == 'enabled':
- if os.path.exists('images/'+self.google[self.colors]+'.icns') == True:
+ if platform is 'Darwin' and self.notifications is 'enabled':
+ if os.path.exists('images/' +
+ self.google[self.colors] +
+ '.icns') is True:
noticon = (
- 'images/'
- + self.google[self.colors]
- + '.icns'
+ 'images/' +
+ self.google[self.colors] +
+ '.icns'
)
else:
noticon = (
- self.google[self.colors]
- + '.icns'
+ self.google[self.colors] +
+ '.icns'
)
searching = [
'./notifier/terminal-notifier.app/Contents/MacOS/terminal-notifier',
@@ -916,26 +877,28 @@ def search_notification(self):
'Searching for Media Streaming Devices...'
]
subprocess.Popen(searching)
- if debug == True:
- print(':::systray:::',searching)
+ if debug is True:
+ print(':::systray:::', searching)
elif platform == 'Linux' and self.notifications == 'enabled':
try:
import gi
gi.require_version('Notify', '0.7')
from gi.repository import Notify
Notify.init('Mkchromecast')
- found=Notify.Notification.new(
+ found = Notify.Notification.new(
'Mkchromecast',
'Searching for Media Streaming Devices...',
'dialog-information'
)
found.show()
except ImportError:
- print('If you want to receive notifications in Linux, install libnotify and python-gobject')
+ print('If you want to receive notifications in Linux, '
+ 'install libnotify and python-gobject.')
+
def main():
menubar()
if __name__ == '__main__':
- checkmktmp()
+ mkchromecast.__init__.checkmktmp()
main()
diff --git a/mkchromecast/tray_threading.py b/mkchromecast/tray_threading.py
index 91c43fec7..d354218c4 100644
--- a/mkchromecast/tray_threading.py
+++ b/mkchromecast/tray_threading.py
@@ -3,32 +3,33 @@
# This file is part of mkchromecast.
import mkchromecast.__init__
-from mkchromecast.audio_devices import *
-from mkchromecast.cast import *
-from mkchromecast.config import *
+from mkchromecast.audio_devices import inputdev, outputdev
+from mkchromecast.cast import casting
+from mkchromecast.config import config_manager
import mkchromecast.audio
-from mkchromecast.node import *
+from mkchromecast.node import stream
from mkchromecast.preferences import ConfigSectionMap
from mkchromecast.pulseaudio import create_sink, check_sink
-from mkchromecast.systray import *
-from PyQt5.QtCore import (QThread, QObject, pyqtSignal, pyqtSlot)
+from PyQt5.QtCore import (QObject, pyqtSignal, pyqtSlot)
import os.path
-import pickle
-import pychromecast
+import socket
+import mkchromecast.colors as colors
"""
Configparser is imported differently in Python3
"""
try:
import ConfigParser
except ImportError:
- import configparser as ConfigParser # This is for Python3
+ import configparser as ConfigParser
platform = mkchromecast.__init__.platform
debug = mkchromecast.__init__.debug
config = ConfigParser.RawConfigParser()
-configurations = config_manager() # Class from mkchromecast.config
+# Class from mkchromecast.config
+configurations = config_manager()
configf = configurations.configf
+
class Worker(QObject):
finished = pyqtSignal()
intReady = pyqtSignal(list)
@@ -38,17 +39,20 @@ def __init__(self):
@pyqtSlot()
def _search_cast_(self):
- try: # This should fix the error socket.gaierror making the system tray to be closed.
+ # This should fix the error socket.gaierror making the system tray to
+ # be closed.
+ try:
self.cc = casting()
self.cc.initialize_cast()
self.cc.availablecc()
except socket.gaierror:
- if debug == True:
- print(colors.warning(':::Threading::: Socket error, CC set to 0'))
+ if debug is True:
+ print(colors.warning(
+ ':::Threading::: Socket error, CC set to 0'))
pass
except TypeError:
pass
- if len(self.cc.availablecc) == 0 and tray == True:
+ if len(self.cc.availablecc) == 0 and tray is True:
availablecc = []
self.intReady.emit(availablecc)
self.finished.emit()
@@ -57,6 +61,7 @@ def _search_cast_(self):
self.intReady.emit(availablecc)
self.finished.emit()
+
class Player(QObject):
pcastfinished = pyqtSignal()
pcastready = pyqtSignal(str)
@@ -67,11 +72,11 @@ def __init__(self):
@pyqtSlot()
def _play_cast_(self):
if os.path.exists(configf):
- print(colors.warning(':::Threading::: Configuration file exists'))
- print(colors.warning(':::Threading::: Using defaults set there'))
+ print(colors.warning(':::Threading::: Configuration file exists.'))
+ print(colors.warning(':::Threading::: Using defaults set there.'))
config.read(configf)
backend = ConfigSectionMap('settings')['backend']
- print(':::Threading backend::: '+backend)
+ print(':::Threading backend::: %s.' % backend)
else:
backend = mkchromecast.__init__.backend
global cast
@@ -85,7 +90,8 @@ def _play_cast_(self):
reload(mkchromecast.audio)
mkchromecast.audio.main()
if platform == 'Linux':
- if check_sink() == False: # We create the sink only if it is not available
+ # We create the sink only if it is not available
+ if check_sink() is False:
create_sink()
start = casting()
@@ -94,14 +100,22 @@ def _play_cast_(self):
start.get_cc()
start.play_cast()
cast = start.cast
- if platform == 'Darwin': # Let's change inputs at the end to avoid muting sound too early.
- inputdev() # For Linux it does not matter given that user has to select sink in pulse audio.
- outputdev() # Therefore the sooner it is available, the better.
+ # Let's change inputs at the end to avoid muting sound too early.
+ # For Linux it does not matter given that user has to select sink
+ # in pulse audio. Therefore the sooner it is available, the
+ # better.
+ if platform == 'Darwin':
+ inputdev()
+ outputdev()
self.pcastready.emit('_play_cast_ success')
except AttributeError:
self.pcastready.emit('_play_cast_ failed')
self.pcastfinished.emit()
+
+url = 'https://api.github.com/repos/muammar/mkchromecast/releases/latest'
+
+
class Updater(QObject):
"""This class is employed to check for new mkchromecast versions"""
upcastfinished = pyqtSignal()
@@ -119,7 +133,6 @@ def _updater_(self):
try:
from mkchromecast.version import __version__
import requests
- url = 'https://api.github.com/repos/muammar/mkchromecast/releases/latest'
response = requests.get(url).text.split(',')
for e in response:
@@ -128,10 +141,10 @@ def _updater_(self):
break
if version > __version__:
- print ('Version ' + version + ' is available to download')
+ print('Version %s is available to download' % version)
self.updateready.emit(version)
else:
- print ('You are up to date')
+ print('You are up to date.')
self.updateready.emit('False')
except UnboundLocalError:
self.updateready.emit('error1')
diff --git a/mkchromecast/utils.py b/mkchromecast/utils.py
index 5363e8f9a..93656d5d7 100644
--- a/mkchromecast/utils.py
+++ b/mkchromecast/utils.py
@@ -5,35 +5,61 @@
from os import getpid
import psutil
import os.path
+import mkchromecast.colors as colors
+try:
+ from urlparse import urlparse
+except:
+ from urllib.parse import urlparse
"""
-These functions are used to kill main processes and child.
-
To call them:
- from mkchromecast.terminate import *
+ from mkchromecast.terminate import name
name()
"""
-def terminate():
- if os.path.exists('/tmp/mkchromecast.tmp') == True:
- os.remove('/tmp/mkchromecast.tmp')
+def terminate():
+ del_tmp()
parent_pid = getpid()
parent = psutil.Process(parent_pid)
- for child in parent.children(recursive=True): # or parent.children() for recursive=False
+ for child in parent.children(recursive=True):
child.kill()
parent.kill()
return
+
+def del_tmp():
+ """Delete files created in /tmp/"""
+ delete_me = ['/tmp/mkchromecast.tmp', '/tmp/mkchromecast.pid']
+
+ print(colors.important('Cleaning up /tmp/...'))
+
+ for f in delete_me:
+ if os.path.exists(f) is True:
+ os.remove(f)
+
+ print(colors.success('[Done]'))
+ return
+
+
def is_installed(name, path, debug):
PATH = path
iterate = PATH.split(':')
for item in iterate:
- verifyif = str(item+'/'+name)
- if os.path.exists(verifyif) == False:
+ verifyif = str(item + '/' + name)
+ if os.path.exists(verifyif) is False:
continue
else:
- if debug == True:
- print('Program '+str(name)+' found in '+str(verifyif))
+ if debug is True:
+ print('Program %s found in %s.' % (name, verifyif))
return True
return
+
+
+def check_url(url):
+ """Check if a URL is correct"""
+ try:
+ result = urlparse(url)
+ return True if [result.scheme, result.netloc, result.path] else False
+ except Exception as e:
+ return False
diff --git a/mkchromecast/version.py b/mkchromecast/version.py
index 7ae729a04..d1308ffef 100644
--- a/mkchromecast/version.py
+++ b/mkchromecast/version.py
@@ -2,4 +2,4 @@
# This file is part of mkchromecast.
-__version__ = '0.3.7.1'
+__version__ = '0.3.8'
diff --git a/mkchromecast/video.py b/mkchromecast/video.py
index 556b70e01..f71bd93e7 100644
--- a/mkchromecast/video.py
+++ b/mkchromecast/video.py
@@ -7,11 +7,10 @@
"""
import mkchromecast.__init__
-from mkchromecast.audio_devices import *
+from mkchromecast.audio_devices import inputint, outputint
import mkchromecast.colors as colors
-from mkchromecast.config import *
from mkchromecast.utils import terminate, is_installed
-from mkchromecast.preferences import ConfigSectionMap
+from mkchromecast.resolution import resolution
import psutil
import getpass
import pickle
@@ -19,15 +18,17 @@
import time
from functools import partial
from subprocess import Popen, PIPE
-from flask import Flask, Response, request
+from flask import Flask, Response
import multiprocessing
import threading
import os
from os import getpid
+
+appendtourl = 'stream'
USER = getpass.getuser()
chunk_size = mkchromecast.__init__.chunk_size
-appendtourl = 'stream'
+user_command = mkchromecast.__init__.command
platform = mkchromecast.__init__.platform
subtitles = mkchromecast.__init__.subtitles
input_file = mkchromecast.__init__.input_file
@@ -36,43 +37,26 @@
debug = mkchromecast.__init__.debug
sourceurl = mkchromecast.__init__.sourceurl
encoder_backend = mkchromecast.__init__.backend
+screencast = mkchromecast.__init__.screencast
+port = mkchromecast.__init__.port
try:
youtubeurl = mkchromecast.__init__.youtubeurl
except AttributeError:
youtubeurl = None
-def resolution(res):
- if res.lower() == '480p':
- insert = ['-vf', 'scale=853:-1']
- return insert
- if res.lower() == '720p':
- insert = ['-vf', 'scale=1280:-1']
- return insert
- if res.lower() == '1080p':
- insert = ['-vf', 'scale=1920:-1']
- return insert
- if res.lower() == '2k':
- insert = ['-vf', 'scale=2048:-1']
- return insert
- if res.lower() == 'uhd':
- insert = ['-vf', 'scale=3840:-1']
- return insert
- if res.lower() == '4k':
- insert = ['-vf', 'scale=4096:-1']
- return insert
def seeking(seek):
seek_append = ['-ss', seek]
for i, _ in enumerate(seek_append):
- command.insert(i + 1, _)
+ command.insert(i + 1, _)
""" This command is not working I found this:
http://stackoverflow.com/questions/12801192/client-closes-connection-when-streaming-m4v-from-apache-to-chrome-with-jplayer.
I think that the command below is sending a file that is too big and the
browser closes the connection.
"""
-if youtubeurl != None:
+if youtubeurl is not None:
command = [
'youtube-dl',
'-o',
@@ -81,91 +65,131 @@ def seeking(seek):
]
mtype = 'video/mp4'
+elif screencast is True:
+ if res is None:
+ screen_size = resolution('1080p', screencast)
+ else:
+ screen_size = resolution(res, screencast)
+ command = [
+ 'ffmpeg',
+ '-f', 'x11grab',
+ '-r', '25',
+ '-s', screen_size,
+ '-i', ':0.0+0,0',
+ '-vcodec', 'libx264',
+ '-preset', 'ultrafast',
+ '-tune', 'zerolatency',
+ '-maxrate', '10000k',
+ '-bufsize', '20000k',
+ '-pix_fmt', 'yuv420p',
+ '-g', '60', # '-c:a', 'copy', '-ac', '2',
+ # '-b', '900k',
+ '-f', 'mp4',
+ '-max_muxing_queue_size', '9999',
+ '-movflags', 'frag_keyframe+empty_moov',
+ 'pipe:1'
+ ]
+ mtype = 'video/mp4'
else:
"""
The blocks shown below are related to input_files
"""
- if input_file != None and subtitles == None:
+ if input_file is not None and subtitles is None:
"""
command = [
'ffmpeg',
'-re',
#'-loglevel', 'panic',
'-i', input_file,
+ '-map_chapters', '-1',
'-preset', 'ultrafast',
'-f', 'mp4',
- '-movflags', 'frag_keyframe+faststart ',
+ '-max_muxing_queue_size', '9999',
+ '-movflags', 'frag_keyframe+empty_moov',
'pipe:1'
]
"""
- # Command taken from https://trac.ffmpeg.org/wiki/EncodingForStreamingSites#Streamingafile
+ # Command taken from
+ # https://trac.ffmpeg.org/wiki/EncodingForStreamingSites#Streamingafile
command = [
'ffmpeg',
'-re',
'-i', input_file,
+ '-map_chapters', '-1',
'-vcodec', 'libx264',
'-preset', 'ultrafast',
'-tune', 'zerolatency',
'-maxrate', '10000k',
'-bufsize', '20000k',
'-pix_fmt', 'yuv420p',
- '-g', '60', #'-c:a', 'copy', '-ac', '2',
- #'-b', '900k',
+ '-g', '60', # '-c:a', 'copy', '-ac', '2',
+ # '-b', '900k',
'-f', 'mp4',
- '-movflags', 'frag_keyframe+faststart ',
+ '-max_muxing_queue_size', '9999',
+ '-movflags', 'frag_keyframe+empty_moov',
'pipe:1'
]
- elif input_file != None and subtitles != None:
+ elif input_file is not None and subtitles is not None:
"""
command = [
'ffmpeg',
'-re',
'-i', input_file,
+ '-map_chapters', '-1',
'-preset', 'ultrafast',
'-f', 'mp4',
- '-movflags', 'frag_keyframe+faststart ',
+ '-max_muxing_queue_size', '9999',
+ '-movflags', 'frag_keyframe+empty_moov',
'-vf', 'subtitles='+subtitles,
'pipe:1'
]
"""
- # Command taken from https://trac.ffmpeg.org/wiki/EncodingForStreamingSites#Streamingafile
+ # Command taken from
+ # https://trac.ffmpeg.org/wiki/EncodingForStreamingSites#Streamingafile
command = [
'ffmpeg',
'-re',
'-i', input_file,
+ '-map_chapters', '-1',
'-vcodec', 'libx264',
'-preset', 'ultrafast',
'-tune', 'zerolatency',
'-maxrate', '10000k',
'-bufsize', '20000k',
'-pix_fmt', 'yuv420p',
- '-g', '60', #'-c:a', 'copy', '-ac', '2',
- #'-b', '900k',
+ '-g', '60', # '-c:a', 'copy', '-ac', '2',
+ # '-b', '900k',
'-f', 'mp4',
- '-movflags', 'frag_keyframe+faststart ',
+ '-max_muxing_queue_size', '9999',
+ '-movflags', 'frag_keyframe+empty_moov',
'-vf', 'subtitles='+subtitles,
'pipe:1'
]
- if seek != None:
+ if user_command is not None:
+ command = user_command
+
+ if seek is not None:
seeking(seek)
- if debug == False and sourceurl == None:
+ if debug is False and sourceurl is None:
command.insert(command.index('-i'), 'panic')
command.insert(command.index('panic'), '-loglevel')
mtype = 'video/mp4'
- if res != None:
+
+ if res is not None:
cindex = command.index(input_file)
- res_elements = resolution(res)
+ res_elements = resolution(res, screencast)
for element in res_elements:
command.insert(-cindex, element)
app = Flask(__name__)
-if debug == True:
- print(':::ffmpeg::: command '+str(command))
+if debug is True:
+ print(':::ffmpeg::: command: %s.' % command)
+
@app.route('/')
def index():
@@ -194,16 +218,20 @@ def shutdown():
return 'Server shutting down...'
"""
+
+
@app.route('/' + appendtourl)
def stream():
process = Popen(command, stdout=PIPE, bufsize=-1)
read_chunk = partial(os.read, process.stdout.fileno(), chunk_size)
return Response(iter(read_chunk, b''), mimetype=mtype)
+
def start_app():
monitor_daemon = monitor()
monitor_daemon.start()
- app.run(host= '0.0.0.0', threaded = True)
+ app.run(host='0.0.0.0', port=port, threaded=True)
+
class multi_proc(object): # I launch ffmpeg in a different process
def __init__(self):
@@ -218,6 +246,8 @@ def start(self):
A normal running of mkchromecast will have 2 threads in the streaming process
when ffmpeg is used.
"""
+
+
class monitor(object):
def __init__(self):
self.monitor_d = threading.Thread(target=monitor_daemon)
@@ -226,26 +256,30 @@ def __init__(self):
def start(self):
self.monitor_d.start()
+
def monitor_daemon():
f = open('/tmp/mkchromecast.pid', 'rb')
- pidnumber=int(pickle.load(f))
- print(colors.options('PID of main process:')+' '+str(pidnumber))
+ pidnumber = int(pickle.load(f))
+ print(colors.options('PID of main process:') + ' ' + str(pidnumber))
- localpid=getpid()
- print(colors.options('PID of streaming process:')+' '+str(localpid))
+ localpid = getpid()
+ print(colors.options('PID of streaming process:') + ' ' + str(localpid))
- while psutil.pid_exists(localpid) == True:
+ while psutil.pid_exists(localpid) is True:
try:
time.sleep(0.5)
- if psutil.pid_exists(pidnumber) == False: # With this I ensure that if the main app fails, everything
- if platform == 'Darwin': # will get back to normal
+ # With this I ensure that if the main app fails, everything# With
+ # this I ensure that if the main app fails, everything
+ # will get back to normal
+ if psutil.pid_exists(pidnumber) is False:
+ if platform == 'Darwin':
inputint()
outputint()
else:
from mkchromecast.pulseaudio import remove_sink
remove_sink()
parent = psutil.Process(localpid)
- for child in parent.children(recursive=True): # or parent.children() for recursive=False
+ for child in parent.children(recursive=True):
child.kill()
parent.kill()
except KeyboardInterrupt:
@@ -258,6 +292,7 @@ def monitor_daemon():
print('OSError')
sys.exit(0)
+
def main():
if encoder_backend != 'node':
st = multi_proc()
@@ -265,24 +300,27 @@ def main():
else:
print('Starting Node')
if platform == 'Darwin':
- PATH ='./bin:./nodejs/bin:/Users/'+str(USER)+'/bin:/usr/local/bin:/usr/local/sbin:/usr/bin:/bin:/usr/sbin:/sbin:/opt/X11/bin:/usr/X11/bin:/usr/games:'+ os.environ['PATH']
+ PATH = './bin:./nodejs/bin:/Users/' + \
+ str(USER) + \
+ '/bin:/usr/local/bin:/usr/local/sbin:/usr/bin:/bin:/usr/sbin:/sbin:/opt/X11/bin:/usr/X11/bin:/usr/games:' + \
+ os.environ['PATH']
else:
PATH = os.environ['PATH']
- if debug == True:
- print('PATH ='+str(PATH))
+ if debug is True:
+ print('PATH = %s.' % PATH)
- if platform == 'Darwin' and os.path.exists('./bin/node') == True:
+ if platform == 'Darwin' and os.path.exists('./bin/node') is True:
webcast = [
'./bin/node',
'./nodejs/html5-video-streamer.js',
input_file
]
elif platform == 'Linux':
- node_names =['node', 'nodejs']
+ node_names = ['node', 'nodejs']
nodejs_dir = ['./nodejs/', '/usr/share/mkchromecast/nodejs/']
for name in node_names:
- if is_installed(name, PATH, debug) == True:
+ if is_installed(name, PATH, debug) is True:
for path in nodejs_dir:
if os.path.isdir(path):
path = path + 'html5-video-streamer.js'
@@ -293,8 +331,9 @@ def main():
]
break
try:
- p = subprocess.Popen(webcast)
+ Popen(webcast)
except:
- print(colors.warning('Nodejs is not installed in your system. Please, install it to use this backend.'))
+ print(colors.warning('Nodejs is not installed in your system. '
+ 'Please, install it to use this backend.'))
print(colors.warning('Closing the application...'))
terminate()
diff --git a/nodejs/README.md b/nodejs/README.md
index d41e8c0f8..7854823bf 100644
--- a/nodejs/README.md
+++ b/nodejs/README.md
@@ -18,3 +18,6 @@ Example:
```
This will install the package at `mkchromecast/nodejs/node-$version`.
+
+**Note**: I am now just using node shipped by Homebrew. I might remove this in
+the future.
diff --git a/nodejs/bin/node b/nodejs/bin/node
index 3543401ce..c8ea45dc6 100755
Binary files a/nodejs/bin/node and b/nodejs/bin/node differ
diff --git a/nodejs/node_modules/accepts/HISTORY.md b/nodejs/node_modules/accepts/HISTORY.md
index 0477ed71c..aaf528172 100644
--- a/nodejs/node_modules/accepts/HISTORY.md
+++ b/nodejs/node_modules/accepts/HISTORY.md
@@ -1,3 +1,9 @@
+1.3.4 / 2017-08-22
+==================
+
+ * deps: mime-types@~2.1.16
+ - deps: mime-db@~1.29.0
+
1.3.3 / 2016-05-02
==================
diff --git a/nodejs/node_modules/accepts/README.md b/nodejs/node_modules/accepts/README.md
index ae36676f2..6a2749a1a 100644
--- a/nodejs/node_modules/accepts/README.md
+++ b/nodejs/node_modules/accepts/README.md
@@ -6,23 +6,31 @@
[![Build Status][travis-image]][travis-url]
[![Test Coverage][coveralls-image]][coveralls-url]
-Higher level content negotiation based on [negotiator](https://www.npmjs.com/package/negotiator). Extracted from [koa](https://www.npmjs.com/package/koa) for general use.
+Higher level content negotiation based on [negotiator](https://www.npmjs.com/package/negotiator).
+Extracted from [koa](https://www.npmjs.com/package/koa) for general use.
In addition to negotiator, it allows:
-- Allows types as an array or arguments list, ie `(['text/html', 'application/json'])` as well as `('text/html', 'application/json')`.
+- Allows types as an array or arguments list, ie `(['text/html', 'application/json'])`
+ as well as `('text/html', 'application/json')`.
- Allows type shorthands such as `json`.
- Returns `false` when no types match
- Treats non-existent headers as `*`
## Installation
+This is a [Node.js](https://nodejs.org/en/) module available through the
+[npm registry](https://www.npmjs.com/). Installation is done using the
+[`npm install` command](https://docs.npmjs.com/getting-started/installing-npm-packages-locally):
+
```sh
-npm install accepts
+$ npm install accepts
```
## API
+
+
```js
var accepts = require('accepts')
```
@@ -88,11 +96,11 @@ server.
var accepts = require('accepts')
var http = require('http')
-function app(req, res) {
+function app (req, res) {
var accept = accepts(req)
// the order of this list is significant; should be server preferred order
- switch(accept.type(['json', 'html'])) {
+ switch (accept.type(['json', 'html'])) {
case 'json':
res.setHeader('Content-Type', 'application/json')
res.write('{"hello":"world!"}')
@@ -126,7 +134,7 @@ curl -I -H'Accept: text/html' http://localhost:3000/
[npm-image]: https://img.shields.io/npm/v/accepts.svg
[npm-url]: https://npmjs.org/package/accepts
[node-version-image]: https://img.shields.io/node/v/accepts.svg
-[node-version-url]: http://nodejs.org/download/
+[node-version-url]: https://nodejs.org/en/download/
[travis-image]: https://img.shields.io/travis/jshttp/accepts/master.svg
[travis-url]: https://travis-ci.org/jshttp/accepts
[coveralls-image]: https://img.shields.io/coveralls/jshttp/accepts/master.svg
diff --git a/nodejs/node_modules/accepts/index.js b/nodejs/node_modules/accepts/index.js
index e80192abf..e9b2f63fb 100644
--- a/nodejs/node_modules/accepts/index.js
+++ b/nodejs/node_modules/accepts/index.js
@@ -29,9 +29,10 @@ module.exports = Accepts
* @public
*/
-function Accepts(req) {
- if (!(this instanceof Accepts))
+function Accepts (req) {
+ if (!(this instanceof Accepts)) {
return new Accepts(req)
+ }
this.headers = req.headers
this.negotiator = new Negotiator(req)
@@ -95,12 +96,18 @@ Accepts.prototype.types = function (types_) {
return this.negotiator.mediaTypes()
}
- if (!this.headers.accept) return types[0];
- var mimes = types.map(extToMime);
- var accepts = this.negotiator.mediaTypes(mimes.filter(validMime));
- var first = accepts[0];
- if (!first) return false;
- return types[mimes.indexOf(first)];
+ // no accept header, return first given type
+ if (!this.headers.accept) {
+ return types[0]
+ }
+
+ var mimes = types.map(extToMime)
+ var accepts = this.negotiator.mediaTypes(mimes.filter(validMime))
+ var first = accepts[0]
+
+ return first
+ ? types[mimes.indexOf(first)]
+ : false
}
/**
@@ -212,7 +219,7 @@ Accepts.prototype.languages = function (languages_) {
* @private
*/
-function extToMime(type) {
+function extToMime (type) {
return type.indexOf('/') === -1
? mime.lookup(type)
: type
@@ -226,6 +233,6 @@ function extToMime(type) {
* @private
*/
-function validMime(type) {
- return typeof type === 'string';
+function validMime (type) {
+ return typeof type === 'string'
}
diff --git a/nodejs/node_modules/accepts/package.json b/nodejs/node_modules/accepts/package.json
index 3eea7ce77..4b11981cf 100644
--- a/nodejs/node_modules/accepts/package.json
+++ b/nodejs/node_modules/accepts/package.json
@@ -1,50 +1,32 @@
{
"_args": [
[
- {
- "raw": "accepts@~1.3.3",
- "scope": null,
- "escapedName": "accepts",
- "name": "accepts",
- "rawSpec": "~1.3.3",
- "spec": ">=1.3.3 <1.4.0",
- "type": "range"
- },
- "/Users/muammar/github/mkchromecast/nodejs/node_modules/express"
+ "accepts@1.3.4",
+ "/Users/muammar/github/mkchromecast/nodejs"
]
],
- "_from": "accepts@>=1.3.3 <1.4.0",
- "_id": "accepts@1.3.3",
- "_inCache": true,
+ "_from": "accepts@1.3.4",
+ "_id": "accepts@1.3.4",
+ "_inBundle": false,
+ "_integrity": "sha1-hiRnWMfdbSGmR0/whKR0DsBesh8=",
"_location": "/accepts",
- "_nodeVersion": "4.4.3",
- "_npmOperationalInternal": {
- "host": "packages-16-east.internal.npmjs.com",
- "tmp": "tmp/accepts-1.3.3.tgz_1462251932032_0.7092335098423064"
- },
- "_npmUser": {
- "name": "dougwilson",
- "email": "doug@somethingdoug.com"
- },
- "_npmVersion": "2.15.1",
"_phantomChildren": {},
"_requested": {
- "raw": "accepts@~1.3.3",
- "scope": null,
- "escapedName": "accepts",
+ "type": "version",
+ "registry": true,
+ "raw": "accepts@1.3.4",
"name": "accepts",
- "rawSpec": "~1.3.3",
- "spec": ">=1.3.3 <1.4.0",
- "type": "range"
+ "escapedName": "accepts",
+ "rawSpec": "1.3.4",
+ "saveSpec": null,
+ "fetchSpec": "1.3.4"
},
"_requiredBy": [
"/express"
],
- "_resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.3.tgz",
- "_shasum": "c3ca7434938648c3e0d9c1e328dd68b622c284ca",
- "_shrinkwrap": null,
- "_spec": "accepts@~1.3.3",
- "_where": "/Users/muammar/github/mkchromecast/nodejs/node_modules/express",
+ "_resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.4.tgz",
+ "_spec": "1.3.4",
+ "_where": "/Users/muammar/github/mkchromecast/nodejs",
"bugs": {
"url": "https://github.com/jshttp/accepts/issues"
},
@@ -60,19 +42,21 @@
}
],
"dependencies": {
- "mime-types": "~2.1.11",
+ "mime-types": "~2.1.16",
"negotiator": "0.6.1"
},
"description": "Higher-level content negotiation",
"devDependencies": {
- "istanbul": "0.4.3",
+ "eslint": "3.19.0",
+ "eslint-config-standard": "10.2.1",
+ "eslint-plugin-import": "2.7.0",
+ "eslint-plugin-markdown": "1.0.0-beta.6",
+ "eslint-plugin-node": "5.1.1",
+ "eslint-plugin-promise": "3.5.0",
+ "eslint-plugin-standard": "3.0.1",
+ "istanbul": "0.4.5",
"mocha": "~1.21.5"
},
- "directories": {},
- "dist": {
- "shasum": "c3ca7434938648c3e0d9c1e328dd68b622c284ca",
- "tarball": "https://registry.npmjs.org/accepts/-/accepts-1.3.3.tgz"
- },
"engines": {
"node": ">= 0.6"
},
@@ -81,7 +65,6 @@
"HISTORY.md",
"index.js"
],
- "gitHead": "3e925b1e65ed7da2798849683d49814680dfa426",
"homepage": "https://github.com/jshttp/accepts#readme",
"keywords": [
"content",
@@ -90,23 +73,16 @@
"accepts"
],
"license": "MIT",
- "maintainers": [
- {
- "name": "dougwilson",
- "email": "doug@somethingdoug.com"
- }
- ],
"name": "accepts",
- "optionalDependencies": {},
- "readme": "ERROR: No README data found!",
"repository": {
"type": "git",
"url": "git+https://github.com/jshttp/accepts.git"
},
"scripts": {
+ "lint": "eslint --plugin markdown --ext js,md .",
"test": "mocha --reporter spec --check-leaks --bail test/",
"test-cov": "istanbul cover node_modules/mocha/bin/_mocha -- --reporter dot --check-leaks test/",
"test-travis": "istanbul cover node_modules/mocha/bin/_mocha --report lcovonly -- --reporter spec --check-leaks test/"
},
- "version": "1.3.3"
+ "version": "1.3.4"
}
diff --git a/nodejs/node_modules/ansi-regex/package.json b/nodejs/node_modules/ansi-regex/package.json
index b9b95f722..681cf2f3a 100644
--- a/nodejs/node_modules/ansi-regex/package.json
+++ b/nodejs/node_modules/ansi-regex/package.json
@@ -1,46 +1,33 @@
{
"_args": [
[
- {
- "raw": "ansi-regex@^0.2.0",
- "scope": null,
- "escapedName": "ansi-regex",
- "name": "ansi-regex",
- "rawSpec": "^0.2.0",
- "spec": ">=0.2.0 <0.3.0",
- "type": "range"
- },
- "/Users/muammar/github/mkchromecast/nodejs/node_modules/has-ansi"
+ "ansi-regex@0.2.1",
+ "/Users/muammar/github/mkchromecast/nodejs"
]
],
- "_from": "ansi-regex@>=0.2.0 <0.3.0",
+ "_from": "ansi-regex@0.2.1",
"_id": "ansi-regex@0.2.1",
- "_inCache": true,
+ "_inBundle": false,
+ "_integrity": "sha1-DY6UaWej2BQ/k+JOKYUl/BsiNfk=",
"_location": "/ansi-regex",
- "_npmUser": {
- "name": "sindresorhus",
- "email": "sindresorhus@gmail.com"
- },
- "_npmVersion": "1.4.9",
"_phantomChildren": {},
"_requested": {
- "raw": "ansi-regex@^0.2.0",
- "scope": null,
- "escapedName": "ansi-regex",
+ "type": "version",
+ "registry": true,
+ "raw": "ansi-regex@0.2.1",
"name": "ansi-regex",
- "rawSpec": "^0.2.0",
- "spec": ">=0.2.0 <0.3.0",
- "type": "range"
+ "escapedName": "ansi-regex",
+ "rawSpec": "0.2.1",
+ "saveSpec": null,
+ "fetchSpec": "0.2.1"
},
"_requiredBy": [
"/has-ansi",
"/strip-ansi"
],
"_resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-0.2.1.tgz",
- "_shasum": "0d8e946967a3d8143f93e24e298525fc1b2235f9",
- "_shrinkwrap": null,
- "_spec": "ansi-regex@^0.2.0",
- "_where": "/Users/muammar/github/mkchromecast/nodejs/node_modules/has-ansi",
+ "_spec": "0.2.1",
+ "_where": "/Users/muammar/github/mkchromecast/nodejs",
"author": {
"name": "Sindre Sorhus",
"email": "sindresorhus@gmail.com",
@@ -49,23 +36,17 @@
"bugs": {
"url": "https://github.com/sindresorhus/ansi-regex/issues"
},
- "dependencies": {},
"description": "Regular expression for matching ANSI escape codes",
"devDependencies": {
"mocha": "*"
},
- "directories": {},
- "dist": {
- "shasum": "0d8e946967a3d8143f93e24e298525fc1b2235f9",
- "tarball": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-0.2.1.tgz"
- },
"engines": {
"node": ">=0.10.0"
},
"files": [
"index.js"
],
- "homepage": "https://github.com/sindresorhus/ansi-regex",
+ "homepage": "https://github.com/sindresorhus/ansi-regex#readme",
"keywords": [
"ansi",
"styles",
@@ -94,18 +75,10 @@
"pattern"
],
"license": "MIT",
- "maintainers": [
- {
- "name": "sindresorhus",
- "email": "sindresorhus@gmail.com"
- }
- ],
"name": "ansi-regex",
- "optionalDependencies": {},
- "readme": "ERROR: No README data found!",
"repository": {
"type": "git",
- "url": "git://github.com/sindresorhus/ansi-regex.git"
+ "url": "git+https://github.com/sindresorhus/ansi-regex.git"
},
"scripts": {
"test": "mocha"
diff --git a/nodejs/node_modules/ansi-styles/package.json b/nodejs/node_modules/ansi-styles/package.json
index 802c666e7..68d32f50b 100644
--- a/nodejs/node_modules/ansi-styles/package.json
+++ b/nodejs/node_modules/ansi-styles/package.json
@@ -1,45 +1,32 @@
{
"_args": [
[
- {
- "raw": "ansi-styles@^1.1.0",
- "scope": null,
- "escapedName": "ansi-styles",
- "name": "ansi-styles",
- "rawSpec": "^1.1.0",
- "spec": ">=1.1.0 <2.0.0",
- "type": "range"
- },
- "/Users/muammar/github/mkchromecast/nodejs/node_modules/chalk"
+ "ansi-styles@1.1.0",
+ "/Users/muammar/github/mkchromecast/nodejs"
]
],
- "_from": "ansi-styles@>=1.1.0 <2.0.0",
+ "_from": "ansi-styles@1.1.0",
"_id": "ansi-styles@1.1.0",
- "_inCache": true,
+ "_inBundle": false,
+ "_integrity": "sha1-6uy/Zs1waIJ2Cy9GkVgrj1XXp94=",
"_location": "/ansi-styles",
- "_npmUser": {
- "name": "sindresorhus",
- "email": "sindresorhus@gmail.com"
- },
- "_npmVersion": "1.4.9",
"_phantomChildren": {},
"_requested": {
- "raw": "ansi-styles@^1.1.0",
- "scope": null,
- "escapedName": "ansi-styles",
+ "type": "version",
+ "registry": true,
+ "raw": "ansi-styles@1.1.0",
"name": "ansi-styles",
- "rawSpec": "^1.1.0",
- "spec": ">=1.1.0 <2.0.0",
- "type": "range"
+ "escapedName": "ansi-styles",
+ "rawSpec": "1.1.0",
+ "saveSpec": null,
+ "fetchSpec": "1.1.0"
},
"_requiredBy": [
"/chalk"
],
"_resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-1.1.0.tgz",
- "_shasum": "eaecbf66cd706882760b2f4691582b8f55d7a7de",
- "_shrinkwrap": null,
- "_spec": "ansi-styles@^1.1.0",
- "_where": "/Users/muammar/github/mkchromecast/nodejs/node_modules/chalk",
+ "_spec": "1.1.0",
+ "_where": "/Users/muammar/github/mkchromecast/nodejs",
"author": {
"name": "Sindre Sorhus",
"email": "sindresorhus@gmail.com",
@@ -48,23 +35,17 @@
"bugs": {
"url": "https://github.com/sindresorhus/ansi-styles/issues"
},
- "dependencies": {},
"description": "ANSI escape codes for styling strings in the terminal",
"devDependencies": {
"mocha": "*"
},
- "directories": {},
- "dist": {
- "shasum": "eaecbf66cd706882760b2f4691582b8f55d7a7de",
- "tarball": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-1.1.0.tgz"
- },
"engines": {
"node": ">=0.10.0"
},
"files": [
"index.js"
],
- "homepage": "https://github.com/sindresorhus/ansi-styles",
+ "homepage": "https://github.com/sindresorhus/ansi-styles#readme",
"keywords": [
"ansi",
"styles",
@@ -88,18 +69,10 @@
"text"
],
"license": "MIT",
- "maintainers": [
- {
- "name": "sindresorhus",
- "email": "sindresorhus@gmail.com"
- }
- ],
"name": "ansi-styles",
- "optionalDependencies": {},
- "readme": "ERROR: No README data found!",
"repository": {
"type": "git",
- "url": "git://github.com/sindresorhus/ansi-styles.git"
+ "url": "git+https://github.com/sindresorhus/ansi-styles.git"
},
"scripts": {
"test": "mocha"
diff --git a/nodejs/node_modules/array-flatten/package.json b/nodejs/node_modules/array-flatten/package.json
index 00604f38c..cf6aa4241 100644
--- a/nodejs/node_modules/array-flatten/package.json
+++ b/nodejs/node_modules/array-flatten/package.json
@@ -1,46 +1,32 @@
{
"_args": [
[
- {
- "raw": "array-flatten@1.1.1",
- "scope": null,
- "escapedName": "array-flatten",
- "name": "array-flatten",
- "rawSpec": "1.1.1",
- "spec": "1.1.1",
- "type": "version"
- },
- "/Users/muammar/github/mkchromecast/nodejs/node_modules/express"
+ "array-flatten@1.1.1",
+ "/Users/muammar/github/mkchromecast/nodejs"
]
],
"_from": "array-flatten@1.1.1",
"_id": "array-flatten@1.1.1",
- "_inCache": true,
+ "_inBundle": false,
+ "_integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=",
"_location": "/array-flatten",
- "_nodeVersion": "2.3.3",
- "_npmUser": {
- "name": "blakeembrey",
- "email": "hello@blakeembrey.com"
- },
- "_npmVersion": "2.11.3",
"_phantomChildren": {},
"_requested": {
+ "type": "version",
+ "registry": true,
"raw": "array-flatten@1.1.1",
- "scope": null,
- "escapedName": "array-flatten",
"name": "array-flatten",
+ "escapedName": "array-flatten",
"rawSpec": "1.1.1",
- "spec": "1.1.1",
- "type": "version"
+ "saveSpec": null,
+ "fetchSpec": "1.1.1"
},
"_requiredBy": [
"/express"
],
"_resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
- "_shasum": "9a5f699051b1e7073328f2a008968b64ea2955d2",
- "_shrinkwrap": null,
- "_spec": "array-flatten@1.1.1",
- "_where": "/Users/muammar/github/mkchromecast/nodejs/node_modules/express",
+ "_spec": "1.1.1",
+ "_where": "/Users/muammar/github/mkchromecast/nodejs",
"author": {
"name": "Blake Embrey",
"email": "hello@blakeembrey.com",
@@ -49,7 +35,6 @@
"bugs": {
"url": "https://github.com/blakeembrey/array-flatten/issues"
},
- "dependencies": {},
"description": "Flatten an array of nested arrays into a single flat array",
"devDependencies": {
"istanbul": "^0.3.13",
@@ -57,16 +42,10 @@
"pre-commit": "^1.0.7",
"standard": "^3.7.3"
},
- "directories": {},
- "dist": {
- "shasum": "9a5f699051b1e7073328f2a008968b64ea2955d2",
- "tarball": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz"
- },
"files": [
"array-flatten.js",
"LICENSE"
],
- "gitHead": "1963a9189229d408e1e8f585a00c8be9edbd1803",
"homepage": "https://github.com/blakeembrey/array-flatten",
"keywords": [
"array",
@@ -76,15 +55,7 @@
],
"license": "MIT",
"main": "array-flatten.js",
- "maintainers": [
- {
- "name": "blakeembrey",
- "email": "hello@blakeembrey.com"
- }
- ],
"name": "array-flatten",
- "optionalDependencies": {},
- "readme": "ERROR: No README data found!",
"repository": {
"type": "git",
"url": "git://github.com/blakeembrey/array-flatten.git"
diff --git a/nodejs/node_modules/bindings/README.md b/nodejs/node_modules/bindings/README.md
index 585cf512b..a1f0d2259 100644
--- a/nodejs/node_modules/bindings/README.md
+++ b/nodejs/node_modules/bindings/README.md
@@ -69,6 +69,7 @@ Error: Could not load the bindings file. Tried:
...
```
+The searching for the `.node` file will originate from the first directory in which has a `package.json` file is found.
License
-------
diff --git a/nodejs/node_modules/bindings/bindings.js b/nodejs/node_modules/bindings/bindings.js
index 93dcf85a1..9a497bf0d 100644
--- a/nodejs/node_modules/bindings/bindings.js
+++ b/nodejs/node_modules/bindings/bindings.js
@@ -7,7 +7,8 @@ var fs = require('fs')
, path = require('path')
, join = path.join
, dirname = path.dirname
- , exists = fs.existsSync || path.existsSync
+ , exists = ((fs.accessSync && function (path) { try { fs.accessSync(path); } catch (e) { return false; } return true; })
+ || fs.existsSync || path.existsSync)
, defaults = {
arrow: process.env.NODE_BINDINGS_ARROW || ' → '
, compiled: process.env.NODE_BINDINGS_COMPILED_DIR || 'compiled'
@@ -48,7 +49,11 @@ function bindings (opts) {
} else if (!opts) {
opts = {}
}
- opts.__proto__ = defaults
+
+ // maps `defaults` onto `opts` object
+ Object.keys(defaults).map(function(i) {
+ if (!(i in opts)) opts[i] = defaults[i];
+ });
// Get the module root
if (!opts.module_root) {
diff --git a/nodejs/node_modules/bindings/package.json b/nodejs/node_modules/bindings/package.json
index 4b289fa90..a3971ad06 100644
--- a/nodejs/node_modules/bindings/package.json
+++ b/nodejs/node_modules/bindings/package.json
@@ -1,46 +1,33 @@
{
"_args": [
[
- {
- "raw": "bindings@^1.2.1",
- "scope": null,
- "escapedName": "bindings",
- "name": "bindings",
- "rawSpec": "^1.2.1",
- "spec": ">=1.2.1 <2.0.0",
- "type": "range"
- },
- "/Users/muammar/github/mkchromecast/nodejs/node_modules/lame"
+ "bindings@1.3.0",
+ "/Users/muammar/github/mkchromecast/nodejs"
]
],
- "_from": "bindings@>=1.2.1 <2.0.0",
- "_id": "bindings@1.2.1",
- "_inCache": true,
+ "_from": "bindings@1.3.0",
+ "_id": "bindings@1.3.0",
+ "_inBundle": false,
+ "_integrity": "sha512-DpLh5EzMR2kzvX1KIlVC0VkC3iZtHKTgdtZ0a3pglBZdaQFjt5S9g9xd1lE+YvXyfd6mtCeRnrUfOLYiTMlNSw==",
"_location": "/bindings",
- "_npmUser": {
- "name": "tootallnate",
- "email": "nathan@tootallnate.net"
- },
- "_npmVersion": "1.4.14",
"_phantomChildren": {},
"_requested": {
- "raw": "bindings@^1.2.1",
- "scope": null,
- "escapedName": "bindings",
+ "type": "version",
+ "registry": true,
+ "raw": "bindings@1.3.0",
"name": "bindings",
- "rawSpec": "^1.2.1",
- "spec": ">=1.2.1 <2.0.0",
- "type": "range"
+ "escapedName": "bindings",
+ "rawSpec": "1.3.0",
+ "saveSpec": null,
+ "fetchSpec": "1.3.0"
},
"_requiredBy": [
"/lame",
"/osx-audio"
],
- "_resolved": "https://registry.npmjs.org/bindings/-/bindings-1.2.1.tgz",
- "_shasum": "14ad6113812d2d37d72e67b4cacb4bb726505f11",
- "_shrinkwrap": null,
- "_spec": "bindings@^1.2.1",
- "_where": "/Users/muammar/github/mkchromecast/nodejs/node_modules/lame",
+ "_resolved": "https://registry.npmjs.org/bindings/-/bindings-1.3.0.tgz",
+ "_spec": "1.3.0",
+ "_where": "/Users/muammar/github/mkchromecast/nodejs",
"author": {
"name": "Nathan Rajlich",
"email": "nathan@tootallnate.net",
@@ -49,15 +36,7 @@
"bugs": {
"url": "https://github.com/TooTallNate/node-bindings/issues"
},
- "dependencies": {},
"description": "Helper module for loading your native module's .node file",
- "devDependencies": {},
- "directories": {},
- "dist": {
- "shasum": "14ad6113812d2d37d72e67b4cacb4bb726505f11",
- "tarball": "https://registry.npmjs.org/bindings/-/bindings-1.2.1.tgz"
- },
- "gitHead": "e404152ee27f8478ccbc7122ee051246e8e5ec02",
"homepage": "https://github.com/TooTallNate/node-bindings",
"keywords": [
"native",
@@ -70,23 +49,10 @@
],
"license": "MIT",
"main": "./bindings.js",
- "maintainers": [
- {
- "name": "TooTallNate",
- "email": "nathan@tootallnate.net"
- },
- {
- "name": "tootallnate",
- "email": "nathan@tootallnate.net"
- }
- ],
"name": "bindings",
- "optionalDependencies": {},
- "readme": "ERROR: No README data found!",
"repository": {
"type": "git",
"url": "git://github.com/TooTallNate/node-bindings.git"
},
- "scripts": {},
- "version": "1.2.1"
+ "version": "1.3.0"
}
diff --git a/nodejs/node_modules/body-parser/HISTORY.md b/nodejs/node_modules/body-parser/HISTORY.md
new file mode 100644
index 000000000..6ab747bb1
--- /dev/null
+++ b/nodejs/node_modules/body-parser/HISTORY.md
@@ -0,0 +1,568 @@
+1.18.2 / 2017-09-22
+===================
+
+ * deps: debug@2.6.9
+ * perf: remove argument reassignment
+
+1.18.1 / 2017-09-12
+===================
+
+ * deps: content-type@~1.0.4
+ - perf: remove argument reassignment
+ - perf: skip parameter parsing when no parameters
+ * deps: iconv-lite@0.4.19
+ - Fix ISO-8859-1 regression
+ - Update Windows-1255
+ * deps: qs@6.5.1
+ - Fix parsing & compacting very deep objects
+ * deps: raw-body@2.3.2
+ - deps: iconv-lite@0.4.19
+
+1.18.0 / 2017-09-08
+===================
+
+ * Fix JSON strict violation error to match native parse error
+ * Include the `body` property on verify errors
+ * Include the `type` property on all generated errors
+ * Use `http-errors` to set status code on errors
+ * deps: bytes@3.0.0
+ * deps: debug@2.6.8
+ * deps: depd@~1.1.1
+ - Remove unnecessary `Buffer` loading
+ * deps: http-errors@~1.6.2
+ - deps: depd@1.1.1
+ * deps: iconv-lite@0.4.18
+ - Add support for React Native
+ - Add a warning if not loaded as utf-8
+ - Fix CESU-8 decoding in Node.js 8
+ - Improve speed of ISO-8859-1 encoding
+ * deps: qs@6.5.0
+ * deps: raw-body@2.3.1
+ - Use `http-errors` for standard emitted errors
+ - deps: bytes@3.0.0
+ - deps: iconv-lite@0.4.18
+ - perf: skip buffer decoding on overage chunk
+ * perf: prevent internal `throw` when missing charset
+
+1.17.2 / 2017-05-17
+===================
+
+ * deps: debug@2.6.7
+ - Fix `DEBUG_MAX_ARRAY_LENGTH`
+ - deps: ms@2.0.0
+ * deps: type-is@~1.6.15
+ - deps: mime-types@~2.1.15
+
+1.17.1 / 2017-03-06
+===================
+
+ * deps: qs@6.4.0
+ - Fix regression parsing keys starting with `[`
+
+1.17.0 / 2017-03-01
+===================
+
+ * deps: http-errors@~1.6.1
+ - Make `message` property enumerable for `HttpError`s
+ - deps: setprototypeof@1.0.3
+ * deps: qs@6.3.1
+ - Fix compacting nested arrays
+
+1.16.1 / 2017-02-10
+===================
+
+ * deps: debug@2.6.1
+ - Fix deprecation messages in WebStorm and other editors
+ - Undeprecate `DEBUG_FD` set to `1` or `2`
+
+1.16.0 / 2017-01-17
+===================
+
+ * deps: debug@2.6.0
+ - Allow colors in workers
+ - Deprecated `DEBUG_FD` environment variable
+ - Fix error when running under React Native
+ - Use same color for same namespace
+ - deps: ms@0.7.2
+ * deps: http-errors@~1.5.1
+ - deps: inherits@2.0.3
+ - deps: setprototypeof@1.0.2
+ - deps: statuses@'>= 1.3.1 < 2'
+ * deps: iconv-lite@0.4.15
+ - Added encoding MS-31J
+ - Added encoding MS-932
+ - Added encoding MS-936
+ - Added encoding MS-949
+ - Added encoding MS-950
+ - Fix GBK/GB18030 handling of Euro character
+ * deps: qs@6.2.1
+ - Fix array parsing from skipping empty values
+ * deps: raw-body@~2.2.0
+ - deps: iconv-lite@0.4.15
+ * deps: type-is@~1.6.14
+ - deps: mime-types@~2.1.13
+
+1.15.2 / 2016-06-19
+===================
+
+ * deps: bytes@2.4.0
+ * deps: content-type@~1.0.2
+ - perf: enable strict mode
+ * deps: http-errors@~1.5.0
+ - Use `setprototypeof` module to replace `__proto__` setting
+ - deps: statuses@'>= 1.3.0 < 2'
+ - perf: enable strict mode
+ * deps: qs@6.2.0
+ * deps: raw-body@~2.1.7
+ - deps: bytes@2.4.0
+ - perf: remove double-cleanup on happy path
+ * deps: type-is@~1.6.13
+ - deps: mime-types@~2.1.11
+
+1.15.1 / 2016-05-05
+===================
+
+ * deps: bytes@2.3.0
+ - Drop partial bytes on all parsed units
+ - Fix parsing byte string that looks like hex
+ * deps: raw-body@~2.1.6
+ - deps: bytes@2.3.0
+ * deps: type-is@~1.6.12
+ - deps: mime-types@~2.1.10
+
+1.15.0 / 2016-02-10
+===================
+
+ * deps: http-errors@~1.4.0
+ - Add `HttpError` export, for `err instanceof createError.HttpError`
+ - deps: inherits@2.0.1
+ - deps: statuses@'>= 1.2.1 < 2'
+ * deps: qs@6.1.0
+ * deps: type-is@~1.6.11
+ - deps: mime-types@~2.1.9
+
+1.14.2 / 2015-12-16
+===================
+
+ * deps: bytes@2.2.0
+ * deps: iconv-lite@0.4.13
+ * deps: qs@5.2.0
+ * deps: raw-body@~2.1.5
+ - deps: bytes@2.2.0
+ - deps: iconv-lite@0.4.13
+ * deps: type-is@~1.6.10
+ - deps: mime-types@~2.1.8
+
+1.14.1 / 2015-09-27
+===================
+
+ * Fix issue where invalid charset results in 400 when `verify` used
+ * deps: iconv-lite@0.4.12
+ - Fix CESU-8 decoding in Node.js 4.x
+ * deps: raw-body@~2.1.4
+ - Fix masking critical errors from `iconv-lite`
+ - deps: iconv-lite@0.4.12
+ * deps: type-is@~1.6.9
+ - deps: mime-types@~2.1.7
+
+1.14.0 / 2015-09-16
+===================
+
+ * Fix JSON strict parse error to match syntax errors
+ * Provide static `require` analysis in `urlencoded` parser
+ * deps: depd@~1.1.0
+ - Support web browser loading
+ * deps: qs@5.1.0
+ * deps: raw-body@~2.1.3
+ - Fix sync callback when attaching data listener causes sync read
+ * deps: type-is@~1.6.8
+ - Fix type error when given invalid type to match against
+ - deps: mime-types@~2.1.6
+
+1.13.3 / 2015-07-31
+===================
+
+ * deps: type-is@~1.6.6
+ - deps: mime-types@~2.1.4
+
+1.13.2 / 2015-07-05
+===================
+
+ * deps: iconv-lite@0.4.11
+ * deps: qs@4.0.0
+ - Fix dropping parameters like `hasOwnProperty`
+ - Fix user-visible incompatibilities from 3.1.0
+ - Fix various parsing edge cases
+ * deps: raw-body@~2.1.2
+ - Fix error stack traces to skip `makeError`
+ - deps: iconv-lite@0.4.11
+ * deps: type-is@~1.6.4
+ - deps: mime-types@~2.1.2
+ - perf: enable strict mode
+ - perf: remove argument reassignment
+
+1.13.1 / 2015-06-16
+===================
+
+ * deps: qs@2.4.2
+ - Downgraded from 3.1.0 because of user-visible incompatibilities
+
+1.13.0 / 2015-06-14
+===================
+
+ * Add `statusCode` property on `Error`s, in addition to `status`
+ * Change `type` default to `application/json` for JSON parser
+ * Change `type` default to `application/x-www-form-urlencoded` for urlencoded parser
+ * Provide static `require` analysis
+ * Use the `http-errors` module to generate errors
+ * deps: bytes@2.1.0
+ - Slight optimizations
+ * deps: iconv-lite@0.4.10
+ - The encoding UTF-16 without BOM now defaults to UTF-16LE when detection fails
+ - Leading BOM is now removed when decoding
+ * deps: on-finished@~2.3.0
+ - Add defined behavior for HTTP `CONNECT` requests
+ - Add defined behavior for HTTP `Upgrade` requests
+ - deps: ee-first@1.1.1
+ * deps: qs@3.1.0
+ - Fix dropping parameters like `hasOwnProperty`
+ - Fix various parsing edge cases
+ - Parsed object now has `null` prototype
+ * deps: raw-body@~2.1.1
+ - Use `unpipe` module for unpiping requests
+ - deps: iconv-lite@0.4.10
+ * deps: type-is@~1.6.3
+ - deps: mime-types@~2.1.1
+ - perf: reduce try block size
+ - perf: remove bitwise operations
+ * perf: enable strict mode
+ * perf: remove argument reassignment
+ * perf: remove delete call
+
+1.12.4 / 2015-05-10
+===================
+
+ * deps: debug@~2.2.0
+ * deps: qs@2.4.2
+ - Fix allowing parameters like `constructor`
+ * deps: on-finished@~2.2.1
+ * deps: raw-body@~2.0.1
+ - Fix a false-positive when unpiping in Node.js 0.8
+ - deps: bytes@2.0.1
+ * deps: type-is@~1.6.2
+ - deps: mime-types@~2.0.11
+
+1.12.3 / 2015-04-15
+===================
+
+ * Slight efficiency improvement when not debugging
+ * deps: depd@~1.0.1
+ * deps: iconv-lite@0.4.8
+ - Add encoding alias UNICODE-1-1-UTF-7
+ * deps: raw-body@1.3.4
+ - Fix hanging callback if request aborts during read
+ - deps: iconv-lite@0.4.8
+
+1.12.2 / 2015-03-16
+===================
+
+ * deps: qs@2.4.1
+ - Fix error when parameter `hasOwnProperty` is present
+
+1.12.1 / 2015-03-15
+===================
+
+ * deps: debug@~2.1.3
+ - Fix high intensity foreground color for bold
+ - deps: ms@0.7.0
+ * deps: type-is@~1.6.1
+ - deps: mime-types@~2.0.10
+
+1.12.0 / 2015-02-13
+===================
+
+ * add `debug` messages
+ * accept a function for the `type` option
+ * use `content-type` to parse `Content-Type` headers
+ * deps: iconv-lite@0.4.7
+ - Gracefully support enumerables on `Object.prototype`
+ * deps: raw-body@1.3.3
+ - deps: iconv-lite@0.4.7
+ * deps: type-is@~1.6.0
+ - fix argument reassignment
+ - fix false-positives in `hasBody` `Transfer-Encoding` check
+ - support wildcard for both type and subtype (`*/*`)
+ - deps: mime-types@~2.0.9
+
+1.11.0 / 2015-01-30
+===================
+
+ * make internal `extended: true` depth limit infinity
+ * deps: type-is@~1.5.6
+ - deps: mime-types@~2.0.8
+
+1.10.2 / 2015-01-20
+===================
+
+ * deps: iconv-lite@0.4.6
+ - Fix rare aliases of single-byte encodings
+ * deps: raw-body@1.3.2
+ - deps: iconv-lite@0.4.6
+
+1.10.1 / 2015-01-01
+===================
+
+ * deps: on-finished@~2.2.0
+ * deps: type-is@~1.5.5
+ - deps: mime-types@~2.0.7
+
+1.10.0 / 2014-12-02
+===================
+
+ * make internal `extended: true` array limit dynamic
+
+1.9.3 / 2014-11-21
+==================
+
+ * deps: iconv-lite@0.4.5
+ - Fix Windows-31J and X-SJIS encoding support
+ * deps: qs@2.3.3
+ - Fix `arrayLimit` behavior
+ * deps: raw-body@1.3.1
+ - deps: iconv-lite@0.4.5
+ * deps: type-is@~1.5.3
+ - deps: mime-types@~2.0.3
+
+1.9.2 / 2014-10-27
+==================
+
+ * deps: qs@2.3.2
+ - Fix parsing of mixed objects and values
+
+1.9.1 / 2014-10-22
+==================
+
+ * deps: on-finished@~2.1.1
+ - Fix handling of pipelined requests
+ * deps: qs@2.3.0
+ - Fix parsing of mixed implicit and explicit arrays
+ * deps: type-is@~1.5.2
+ - deps: mime-types@~2.0.2
+
+1.9.0 / 2014-09-24
+==================
+
+ * include the charset in "unsupported charset" error message
+ * include the encoding in "unsupported content encoding" error message
+ * deps: depd@~1.0.0
+
+1.8.4 / 2014-09-23
+==================
+
+ * fix content encoding to be case-insensitive
+
+1.8.3 / 2014-09-19
+==================
+
+ * deps: qs@2.2.4
+ - Fix issue with object keys starting with numbers truncated
+
+1.8.2 / 2014-09-15
+==================
+
+ * deps: depd@0.4.5
+
+1.8.1 / 2014-09-07
+==================
+
+ * deps: media-typer@0.3.0
+ * deps: type-is@~1.5.1
+
+1.8.0 / 2014-09-05
+==================
+
+ * make empty-body-handling consistent between chunked requests
+ - empty `json` produces `{}`
+ - empty `raw` produces `new Buffer(0)`
+ - empty `text` produces `''`
+ - empty `urlencoded` produces `{}`
+ * deps: qs@2.2.3
+ - Fix issue where first empty value in array is discarded
+ * deps: type-is@~1.5.0
+ - fix `hasbody` to be true for `content-length: 0`
+
+1.7.0 / 2014-09-01
+==================
+
+ * add `parameterLimit` option to `urlencoded` parser
+ * change `urlencoded` extended array limit to 100
+ * respond with 413 when over `parameterLimit` in `urlencoded`
+
+1.6.7 / 2014-08-29
+==================
+
+ * deps: qs@2.2.2
+ - Remove unnecessary cloning
+
+1.6.6 / 2014-08-27
+==================
+
+ * deps: qs@2.2.0
+ - Array parsing fix
+ - Performance improvements
+
+1.6.5 / 2014-08-16
+==================
+
+ * deps: on-finished@2.1.0
+
+1.6.4 / 2014-08-14
+==================
+
+ * deps: qs@1.2.2
+
+1.6.3 / 2014-08-10
+==================
+
+ * deps: qs@1.2.1
+
+1.6.2 / 2014-08-07
+==================
+
+ * deps: qs@1.2.0
+ - Fix parsing array of objects
+
+1.6.1 / 2014-08-06
+==================
+
+ * deps: qs@1.1.0
+ - Accept urlencoded square brackets
+ - Accept empty values in implicit array notation
+
+1.6.0 / 2014-08-05
+==================
+
+ * deps: qs@1.0.2
+ - Complete rewrite
+ - Limits array length to 20
+ - Limits object depth to 5
+ - Limits parameters to 1,000
+
+1.5.2 / 2014-07-27
+==================
+
+ * deps: depd@0.4.4
+ - Work-around v8 generating empty stack traces
+
+1.5.1 / 2014-07-26
+==================
+
+ * deps: depd@0.4.3
+ - Fix exception when global `Error.stackTraceLimit` is too low
+
+1.5.0 / 2014-07-20
+==================
+
+ * deps: depd@0.4.2
+ - Add `TRACE_DEPRECATION` environment variable
+ - Remove non-standard grey color from color output
+ - Support `--no-deprecation` argument
+ - Support `--trace-deprecation` argument
+ * deps: iconv-lite@0.4.4
+ - Added encoding UTF-7
+ * deps: raw-body@1.3.0
+ - deps: iconv-lite@0.4.4
+ - Added encoding UTF-7
+ - Fix `Cannot switch to old mode now` error on Node.js 0.10+
+ * deps: type-is@~1.3.2
+
+1.4.3 / 2014-06-19
+==================
+
+ * deps: type-is@1.3.1
+ - fix global variable leak
+
+1.4.2 / 2014-06-19
+==================
+
+ * deps: type-is@1.3.0
+ - improve type parsing
+
+1.4.1 / 2014-06-19
+==================
+
+ * fix urlencoded extended deprecation message
+
+1.4.0 / 2014-06-19
+==================
+
+ * add `text` parser
+ * add `raw` parser
+ * check accepted charset in content-type (accepts utf-8)
+ * check accepted encoding in content-encoding (accepts identity)
+ * deprecate `bodyParser()` middleware; use `.json()` and `.urlencoded()` as needed
+ * deprecate `urlencoded()` without provided `extended` option
+ * lazy-load urlencoded parsers
+ * parsers split into files for reduced mem usage
+ * support gzip and deflate bodies
+ - set `inflate: false` to turn off
+ * deps: raw-body@1.2.2
+ - Support all encodings from `iconv-lite`
+
+1.3.1 / 2014-06-11
+==================
+
+ * deps: type-is@1.2.1
+ - Switch dependency from mime to mime-types@1.0.0
+
+1.3.0 / 2014-05-31
+==================
+
+ * add `extended` option to urlencoded parser
+
+1.2.2 / 2014-05-27
+==================
+
+ * deps: raw-body@1.1.6
+ - assert stream encoding on node.js 0.8
+ - assert stream encoding on node.js < 0.10.6
+ - deps: bytes@1
+
+1.2.1 / 2014-05-26
+==================
+
+ * invoke `next(err)` after request fully read
+ - prevents hung responses and socket hang ups
+
+1.2.0 / 2014-05-11
+==================
+
+ * add `verify` option
+ * deps: type-is@1.2.0
+ - support suffix matching
+
+1.1.2 / 2014-05-11
+==================
+
+ * improve json parser speed
+
+1.1.1 / 2014-05-11
+==================
+
+ * fix repeated limit parsing with every request
+
+1.1.0 / 2014-05-10
+==================
+
+ * add `type` option
+ * deps: pin for safety and consistency
+
+1.0.2 / 2014-04-14
+==================
+
+ * use `type-is` module
+
+1.0.1 / 2014-03-20
+==================
+
+ * lower default limits to 100kb
diff --git a/nodejs/node_modules/body-parser/LICENSE b/nodejs/node_modules/body-parser/LICENSE
new file mode 100644
index 000000000..386b7b694
--- /dev/null
+++ b/nodejs/node_modules/body-parser/LICENSE
@@ -0,0 +1,23 @@
+(The MIT License)
+
+Copyright (c) 2014 Jonathan Ong
+Copyright (c) 2014-2015 Douglas Christopher Wilson
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+'Software'), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/nodejs/node_modules/body-parser/README.md b/nodejs/node_modules/body-parser/README.md
new file mode 100644
index 000000000..62221e473
--- /dev/null
+++ b/nodejs/node_modules/body-parser/README.md
@@ -0,0 +1,438 @@
+# body-parser
+
+[![NPM Version][npm-image]][npm-url]
+[![NPM Downloads][downloads-image]][downloads-url]
+[![Build Status][travis-image]][travis-url]
+[![Test Coverage][coveralls-image]][coveralls-url]
+[![Gratipay][gratipay-image]][gratipay-url]
+
+Node.js body parsing middleware.
+
+Parse incoming request bodies in a middleware before your handlers, available
+under the `req.body` property.
+
+[Learn about the anatomy of an HTTP transaction in Node.js](https://nodejs.org/en/docs/guides/anatomy-of-an-http-transaction/).
+
+_This does not handle multipart bodies_, due to their complex and typically
+large nature. For multipart bodies, you may be interested in the following
+modules:
+
+ * [busboy](https://www.npmjs.org/package/busboy#readme) and
+ [connect-busboy](https://www.npmjs.org/package/connect-busboy#readme)
+ * [multiparty](https://www.npmjs.org/package/multiparty#readme) and
+ [connect-multiparty](https://www.npmjs.org/package/connect-multiparty#readme)
+ * [formidable](https://www.npmjs.org/package/formidable#readme)
+ * [multer](https://www.npmjs.org/package/multer#readme)
+
+This module provides the following parsers:
+
+ * [JSON body parser](#bodyparserjsonoptions)
+ * [Raw body parser](#bodyparserrawoptions)
+ * [Text body parser](#bodyparsertextoptions)
+ * [URL-encoded form body parser](#bodyparserurlencodedoptions)
+
+Other body parsers you might be interested in:
+
+- [body](https://www.npmjs.org/package/body#readme)
+- [co-body](https://www.npmjs.org/package/co-body#readme)
+
+## Installation
+
+```sh
+$ npm install body-parser
+```
+
+## API
+
+
+
+```js
+var bodyParser = require('body-parser')
+```
+
+The `bodyParser` object exposes various factories to create middlewares. All
+middlewares will populate the `req.body` property with the parsed body when
+the `Content-Type` request header matches the `type` option, or an empty
+object (`{}`) if there was no body to parse, the `Content-Type` was not matched,
+or an error occurred.
+
+The various errors returned by this module are described in the
+[errors section](#errors).
+
+### bodyParser.json([options])
+
+Returns middleware that only parses `json` and only looks at requests where
+the `Content-Type` header matches the `type` option. This parser accepts any
+Unicode encoding of the body and supports automatic inflation of `gzip` and
+`deflate` encodings.
+
+A new `body` object containing the parsed data is populated on the `request`
+object after the middleware (i.e. `req.body`).
+
+#### Options
+
+The `json` function takes an optional `options` object that may contain any of
+the following keys:
+
+##### inflate
+
+When set to `true`, then deflated (compressed) bodies will be inflated; when
+`false`, deflated bodies are rejected. Defaults to `true`.
+
+##### limit
+
+Controls the maximum request body size. If this is a number, then the value
+specifies the number of bytes; if it is a string, the value is passed to the
+[bytes](https://www.npmjs.com/package/bytes) library for parsing. Defaults
+to `'100kb'`.
+
+##### reviver
+
+The `reviver` option is passed directly to `JSON.parse` as the second
+argument. You can find more information on this argument
+[in the MDN documentation about JSON.parse](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse#Example.3A_Using_the_reviver_parameter).
+
+##### strict
+
+When set to `true`, will only accept arrays and objects; when `false` will
+accept anything `JSON.parse` accepts. Defaults to `true`.
+
+##### type
+
+The `type` option is used to determine what media type the middleware will
+parse. This option can be a function or a string. If a string, `type` option
+is passed directly to the [type-is](https://www.npmjs.org/package/type-is#readme)
+library and this can be an extension name (like `json`), a mime type (like
+`application/json`), or a mime type with a wildcard (like `*/*` or `*/json`).
+If a function, the `type` option is called as `fn(req)` and the request is
+parsed if it returns a truthy value. Defaults to `application/json`.
+
+##### verify
+
+The `verify` option, if supplied, is called as `verify(req, res, buf, encoding)`,
+where `buf` is a `Buffer` of the raw request body and `encoding` is the
+encoding of the request. The parsing can be aborted by throwing an error.
+
+### bodyParser.raw([options])
+
+Returns middleware that parses all bodies as a `Buffer` and only looks at
+requests where the `Content-Type` header matches the `type` option. This
+parser supports automatic inflation of `gzip` and `deflate` encodings.
+
+A new `body` object containing the parsed data is populated on the `request`
+object after the middleware (i.e. `req.body`). This will be a `Buffer` object
+of the body.
+
+#### Options
+
+The `raw` function takes an optional `options` object that may contain any of
+the following keys:
+
+##### inflate
+
+When set to `true`, then deflated (compressed) bodies will be inflated; when
+`false`, deflated bodies are rejected. Defaults to `true`.
+
+##### limit
+
+Controls the maximum request body size. If this is a number, then the value
+specifies the number of bytes; if it is a string, the value is passed to the
+[bytes](https://www.npmjs.com/package/bytes) library for parsing. Defaults
+to `'100kb'`.
+
+##### type
+
+The `type` option is used to determine what media type the middleware will
+parse. This option can be a function or a string. If a string, `type` option
+is passed directly to the [type-is](https://www.npmjs.org/package/type-is#readme)
+library and this can be an extension name (like `bin`), a mime type (like
+`application/octet-stream`), or a mime type with a wildcard (like `*/*` or
+`application/*`). If a function, the `type` option is called as `fn(req)`
+and the request is parsed if it returns a truthy value. Defaults to
+`application/octet-stream`.
+
+##### verify
+
+The `verify` option, if supplied, is called as `verify(req, res, buf, encoding)`,
+where `buf` is a `Buffer` of the raw request body and `encoding` is the
+encoding of the request. The parsing can be aborted by throwing an error.
+
+### bodyParser.text([options])
+
+Returns middleware that parses all bodies as a string and only looks at
+requests where the `Content-Type` header matches the `type` option. This
+parser supports automatic inflation of `gzip` and `deflate` encodings.
+
+A new `body` string containing the parsed data is populated on the `request`
+object after the middleware (i.e. `req.body`). This will be a string of the
+body.
+
+#### Options
+
+The `text` function takes an optional `options` object that may contain any of
+the following keys:
+
+##### defaultCharset
+
+Specify the default character set for the text content if the charset is not
+specified in the `Content-Type` header of the request. Defaults to `utf-8`.
+
+##### inflate
+
+When set to `true`, then deflated (compressed) bodies will be inflated; when
+`false`, deflated bodies are rejected. Defaults to `true`.
+
+##### limit
+
+Controls the maximum request body size. If this is a number, then the value
+specifies the number of bytes; if it is a string, the value is passed to the
+[bytes](https://www.npmjs.com/package/bytes) library for parsing. Defaults
+to `'100kb'`.
+
+##### type
+
+The `type` option is used to determine what media type the middleware will
+parse. This option can be a function or a string. If a string, `type` option
+is passed directly to the [type-is](https://www.npmjs.org/package/type-is#readme)
+library and this can be an extension name (like `txt`), a mime type (like
+`text/plain`), or a mime type with a wildcard (like `*/*` or `text/*`).
+If a function, the `type` option is called as `fn(req)` and the request is
+parsed if it returns a truthy value. Defaults to `text/plain`.
+
+##### verify
+
+The `verify` option, if supplied, is called as `verify(req, res, buf, encoding)`,
+where `buf` is a `Buffer` of the raw request body and `encoding` is the
+encoding of the request. The parsing can be aborted by throwing an error.
+
+### bodyParser.urlencoded([options])
+
+Returns middleware that only parses `urlencoded` bodies and only looks at
+requests where the `Content-Type` header matches the `type` option. This
+parser accepts only UTF-8 encoding of the body and supports automatic
+inflation of `gzip` and `deflate` encodings.
+
+A new `body` object containing the parsed data is populated on the `request`
+object after the middleware (i.e. `req.body`). This object will contain
+key-value pairs, where the value can be a string or array (when `extended` is
+`false`), or any type (when `extended` is `true`).
+
+#### Options
+
+The `urlencoded` function takes an optional `options` object that may contain
+any of the following keys:
+
+##### extended
+
+The `extended` option allows to choose between parsing the URL-encoded data
+with the `querystring` library (when `false`) or the `qs` library (when
+`true`). The "extended" syntax allows for rich objects and arrays to be
+encoded into the URL-encoded format, allowing for a JSON-like experience
+with URL-encoded. For more information, please
+[see the qs library](https://www.npmjs.org/package/qs#readme).
+
+Defaults to `true`, but using the default has been deprecated. Please
+research into the difference between `qs` and `querystring` and choose the
+appropriate setting.
+
+##### inflate
+
+When set to `true`, then deflated (compressed) bodies will be inflated; when
+`false`, deflated bodies are rejected. Defaults to `true`.
+
+##### limit
+
+Controls the maximum request body size. If this is a number, then the value
+specifies the number of bytes; if it is a string, the value is passed to the
+[bytes](https://www.npmjs.com/package/bytes) library for parsing. Defaults
+to `'100kb'`.
+
+##### parameterLimit
+
+The `parameterLimit` option controls the maximum number of parameters that
+are allowed in the URL-encoded data. If a request contains more parameters
+than this value, a 413 will be returned to the client. Defaults to `1000`.
+
+##### type
+
+The `type` option is used to determine what media type the middleware will
+parse. This option can be a function or a string. If a string, `type` option
+is passed directly to the [type-is](https://www.npmjs.org/package/type-is#readme)
+library and this can be an extension name (like `urlencoded`), a mime type (like
+`application/x-www-form-urlencoded`), or a mime type with a wildcard (like
+`*/x-www-form-urlencoded`). If a function, the `type` option is called as
+`fn(req)` and the request is parsed if it returns a truthy value. Defaults
+to `application/x-www-form-urlencoded`.
+
+##### verify
+
+The `verify` option, if supplied, is called as `verify(req, res, buf, encoding)`,
+where `buf` is a `Buffer` of the raw request body and `encoding` is the
+encoding of the request. The parsing can be aborted by throwing an error.
+
+## Errors
+
+The middlewares provided by this module create errors depending on the error
+condition during parsing. The errors will typically have a `status`/`statusCode`
+property that contains the suggested HTTP response code, an `expose` property
+to determine if the `message` property should be displayed to the client, a
+`type` property to determine the type of error without matching against the
+`message`, and a `body` property containing the read body, if available.
+
+The following are the common errors emitted, though any error can come through
+for various reasons.
+
+### content encoding unsupported
+
+This error will occur when the request had a `Content-Encoding` header that
+contained an encoding but the "inflation" option was set to `false`. The
+`status` property is set to `415`, the `type` property is set to
+`'encoding.unsupported'`, and the `charset` property will be set to the
+encoding that is unsupported.
+
+### request aborted
+
+This error will occur when the request is aborted by the client before reading
+the body has finished. The `received` property will be set to the number of
+bytes received before the request was aborted and the `expected` property is
+set to the number of expected bytes. The `status` property is set to `400`
+and `type` property is set to `'request.aborted'`.
+
+### request entity too large
+
+This error will occur when the request body's size is larger than the "limit"
+option. The `limit` property will be set to the byte limit and the `length`
+property will be set to the request body's length. The `status` property is
+set to `413` and the `type` property is set to `'entity.too.large'`.
+
+### request size did not match content length
+
+This error will occur when the request's length did not match the length from
+the `Content-Length` header. This typically occurs when the request is malformed,
+typically when the `Content-Length` header was calculated based on characters
+instead of bytes. The `status` property is set to `400` and the `type` property
+is set to `'request.size.invalid'`.
+
+### stream encoding should not be set
+
+This error will occur when something called the `req.setEncoding` method prior
+to this middleware. This module operates directly on bytes only and you cannot
+call `req.setEncoding` when using this module. The `status` property is set to
+`500` and the `type` property is set to `'stream.encoding.set'`.
+
+### too many parameters
+
+This error will occur when the content of the request exceeds the configured
+`parameterLimit` for the `urlencoded` parser. The `status` property is set to
+`413` and the `type` property is set to `'parameters.too.many'`.
+
+### unsupported charset "BOGUS"
+
+This error will occur when the request had a charset parameter in the
+`Content-Type` header, but the `iconv-lite` module does not support it OR the
+parser does not support it. The charset is contained in the message as well
+as in the `charset` property. The `status` property is set to `415`, the
+`type` property is set to `'charset.unsupported'`, and the `charset` property
+is set to the charset that is unsupported.
+
+### unsupported content encoding "bogus"
+
+This error will occur when the request had a `Content-Encoding` header that
+contained an unsupported encoding. The encoding is contained in the message
+as well as in the `encoding` property. The `status` property is set to `415`,
+the `type` property is set to `'encoding.unsupported'`, and the `encoding`
+property is set to the encoding that is unsupported.
+
+## Examples
+
+### Express/Connect top-level generic
+
+This example demonstrates adding a generic JSON and URL-encoded parser as a
+top-level middleware, which will parse the bodies of all incoming requests.
+This is the simplest setup.
+
+```js
+var express = require('express')
+var bodyParser = require('body-parser')
+
+var app = express()
+
+// parse application/x-www-form-urlencoded
+app.use(bodyParser.urlencoded({ extended: false }))
+
+// parse application/json
+app.use(bodyParser.json())
+
+app.use(function (req, res) {
+ res.setHeader('Content-Type', 'text/plain')
+ res.write('you posted:\n')
+ res.end(JSON.stringify(req.body, null, 2))
+})
+```
+
+### Express route-specific
+
+This example demonstrates adding body parsers specifically to the routes that
+need them. In general, this is the most recommended way to use body-parser with
+Express.
+
+```js
+var express = require('express')
+var bodyParser = require('body-parser')
+
+var app = express()
+
+// create application/json parser
+var jsonParser = bodyParser.json()
+
+// create application/x-www-form-urlencoded parser
+var urlencodedParser = bodyParser.urlencoded({ extended: false })
+
+// POST /login gets urlencoded bodies
+app.post('/login', urlencodedParser, function (req, res) {
+ if (!req.body) return res.sendStatus(400)
+ res.send('welcome, ' + req.body.username)
+})
+
+// POST /api/users gets JSON bodies
+app.post('/api/users', jsonParser, function (req, res) {
+ if (!req.body) return res.sendStatus(400)
+ // create user in req.body
+})
+```
+
+### Change accepted type for parsers
+
+All the parsers accept a `type` option which allows you to change the
+`Content-Type` that the middleware will parse.
+
+```js
+var express = require('express')
+var bodyParser = require('body-parser')
+
+var app = express()
+
+// parse various different custom JSON types as JSON
+app.use(bodyParser.json({ type: 'application/*+json' }))
+
+// parse some custom thing into a Buffer
+app.use(bodyParser.raw({ type: 'application/vnd.custom-type' }))
+
+// parse an HTML body into a string
+app.use(bodyParser.text({ type: 'text/html' }))
+```
+
+## License
+
+[MIT](LICENSE)
+
+[npm-image]: https://img.shields.io/npm/v/body-parser.svg
+[npm-url]: https://npmjs.org/package/body-parser
+[travis-image]: https://img.shields.io/travis/expressjs/body-parser/master.svg
+[travis-url]: https://travis-ci.org/expressjs/body-parser
+[coveralls-image]: https://img.shields.io/coveralls/expressjs/body-parser/master.svg
+[coveralls-url]: https://coveralls.io/r/expressjs/body-parser?branch=master
+[downloads-image]: https://img.shields.io/npm/dm/body-parser.svg
+[downloads-url]: https://npmjs.org/package/body-parser
+[gratipay-image]: https://img.shields.io/gratipay/dougwilson.svg
+[gratipay-url]: https://www.gratipay.com/dougwilson/
diff --git a/nodejs/node_modules/body-parser/index.js b/nodejs/node_modules/body-parser/index.js
new file mode 100644
index 000000000..93c3a1fff
--- /dev/null
+++ b/nodejs/node_modules/body-parser/index.js
@@ -0,0 +1,157 @@
+/*!
+ * body-parser
+ * Copyright(c) 2014-2015 Douglas Christopher Wilson
+ * MIT Licensed
+ */
+
+'use strict'
+
+/**
+ * Module dependencies.
+ * @private
+ */
+
+var deprecate = require('depd')('body-parser')
+
+/**
+ * Cache of loaded parsers.
+ * @private
+ */
+
+var parsers = Object.create(null)
+
+/**
+ * @typedef Parsers
+ * @type {function}
+ * @property {function} json
+ * @property {function} raw
+ * @property {function} text
+ * @property {function} urlencoded
+ */
+
+/**
+ * Module exports.
+ * @type {Parsers}
+ */
+
+exports = module.exports = deprecate.function(bodyParser,
+ 'bodyParser: use individual json/urlencoded middlewares')
+
+/**
+ * JSON parser.
+ * @public
+ */
+
+Object.defineProperty(exports, 'json', {
+ configurable: true,
+ enumerable: true,
+ get: createParserGetter('json')
+})
+
+/**
+ * Raw parser.
+ * @public
+ */
+
+Object.defineProperty(exports, 'raw', {
+ configurable: true,
+ enumerable: true,
+ get: createParserGetter('raw')
+})
+
+/**
+ * Text parser.
+ * @public
+ */
+
+Object.defineProperty(exports, 'text', {
+ configurable: true,
+ enumerable: true,
+ get: createParserGetter('text')
+})
+
+/**
+ * URL-encoded parser.
+ * @public
+ */
+
+Object.defineProperty(exports, 'urlencoded', {
+ configurable: true,
+ enumerable: true,
+ get: createParserGetter('urlencoded')
+})
+
+/**
+ * Create a middleware to parse json and urlencoded bodies.
+ *
+ * @param {object} [options]
+ * @return {function}
+ * @deprecated
+ * @public
+ */
+
+function bodyParser (options) {
+ var opts = {}
+
+ // exclude type option
+ if (options) {
+ for (var prop in options) {
+ if (prop !== 'type') {
+ opts[prop] = options[prop]
+ }
+ }
+ }
+
+ var _urlencoded = exports.urlencoded(opts)
+ var _json = exports.json(opts)
+
+ return function bodyParser (req, res, next) {
+ _json(req, res, function (err) {
+ if (err) return next(err)
+ _urlencoded(req, res, next)
+ })
+ }
+}
+
+/**
+ * Create a getter for loading a parser.
+ * @private
+ */
+
+function createParserGetter (name) {
+ return function get () {
+ return loadParser(name)
+ }
+}
+
+/**
+ * Load a parser module.
+ * @private
+ */
+
+function loadParser (parserName) {
+ var parser = parsers[parserName]
+
+ if (parser !== undefined) {
+ return parser
+ }
+
+ // this uses a switch for static require analysis
+ switch (parserName) {
+ case 'json':
+ parser = require('./lib/types/json')
+ break
+ case 'raw':
+ parser = require('./lib/types/raw')
+ break
+ case 'text':
+ parser = require('./lib/types/text')
+ break
+ case 'urlencoded':
+ parser = require('./lib/types/urlencoded')
+ break
+ }
+
+ // store to prevent invoking require()
+ return (parsers[parserName] = parser)
+}
diff --git a/nodejs/node_modules/body-parser/lib/read.js b/nodejs/node_modules/body-parser/lib/read.js
new file mode 100644
index 000000000..c10260958
--- /dev/null
+++ b/nodejs/node_modules/body-parser/lib/read.js
@@ -0,0 +1,181 @@
+/*!
+ * body-parser
+ * Copyright(c) 2014-2015 Douglas Christopher Wilson
+ * MIT Licensed
+ */
+
+'use strict'
+
+/**
+ * Module dependencies.
+ * @private
+ */
+
+var createError = require('http-errors')
+var getBody = require('raw-body')
+var iconv = require('iconv-lite')
+var onFinished = require('on-finished')
+var zlib = require('zlib')
+
+/**
+ * Module exports.
+ */
+
+module.exports = read
+
+/**
+ * Read a request into a buffer and parse.
+ *
+ * @param {object} req
+ * @param {object} res
+ * @param {function} next
+ * @param {function} parse
+ * @param {function} debug
+ * @param {object} options
+ * @private
+ */
+
+function read (req, res, next, parse, debug, options) {
+ var length
+ var opts = options
+ var stream
+
+ // flag as parsed
+ req._body = true
+
+ // read options
+ var encoding = opts.encoding !== null
+ ? opts.encoding
+ : null
+ var verify = opts.verify
+
+ try {
+ // get the content stream
+ stream = contentstream(req, debug, opts.inflate)
+ length = stream.length
+ stream.length = undefined
+ } catch (err) {
+ return next(err)
+ }
+
+ // set raw-body options
+ opts.length = length
+ opts.encoding = verify
+ ? null
+ : encoding
+
+ // assert charset is supported
+ if (opts.encoding === null && encoding !== null && !iconv.encodingExists(encoding)) {
+ return next(createError(415, 'unsupported charset "' + encoding.toUpperCase() + '"', {
+ charset: encoding.toLowerCase(),
+ type: 'charset.unsupported'
+ }))
+ }
+
+ // read body
+ debug('read body')
+ getBody(stream, opts, function (error, body) {
+ if (error) {
+ var _error
+
+ if (error.type === 'encoding.unsupported') {
+ // echo back charset
+ _error = createError(415, 'unsupported charset "' + encoding.toUpperCase() + '"', {
+ charset: encoding.toLowerCase(),
+ type: 'charset.unsupported'
+ })
+ } else {
+ // set status code on error
+ _error = createError(400, error)
+ }
+
+ // read off entire request
+ stream.resume()
+ onFinished(req, function onfinished () {
+ next(createError(400, _error))
+ })
+ return
+ }
+
+ // verify
+ if (verify) {
+ try {
+ debug('verify body')
+ verify(req, res, body, encoding)
+ } catch (err) {
+ next(createError(403, err, {
+ body: body,
+ type: err.type || 'entity.verify.failed'
+ }))
+ return
+ }
+ }
+
+ // parse
+ var str = body
+ try {
+ debug('parse body')
+ str = typeof body !== 'string' && encoding !== null
+ ? iconv.decode(body, encoding)
+ : body
+ req.body = parse(str)
+ } catch (err) {
+ next(createError(400, err, {
+ body: str,
+ type: err.type || 'entity.parse.failed'
+ }))
+ return
+ }
+
+ next()
+ })
+}
+
+/**
+ * Get the content stream of the request.
+ *
+ * @param {object} req
+ * @param {function} debug
+ * @param {boolean} [inflate=true]
+ * @return {object}
+ * @api private
+ */
+
+function contentstream (req, debug, inflate) {
+ var encoding = (req.headers['content-encoding'] || 'identity').toLowerCase()
+ var length = req.headers['content-length']
+ var stream
+
+ debug('content-encoding "%s"', encoding)
+
+ if (inflate === false && encoding !== 'identity') {
+ throw createError(415, 'content encoding unsupported', {
+ encoding: encoding,
+ type: 'encoding.unsupported'
+ })
+ }
+
+ switch (encoding) {
+ case 'deflate':
+ stream = zlib.createInflate()
+ debug('inflate body')
+ req.pipe(stream)
+ break
+ case 'gzip':
+ stream = zlib.createGunzip()
+ debug('gunzip body')
+ req.pipe(stream)
+ break
+ case 'identity':
+ stream = req
+ stream.length = length
+ break
+ default:
+ throw createError(415, 'unsupported content encoding "' + encoding + '"', {
+ encoding: encoding,
+ type: 'encoding.unsupported'
+ })
+ }
+
+ return stream
+}
diff --git a/nodejs/node_modules/body-parser/lib/types/json.js b/nodejs/node_modules/body-parser/lib/types/json.js
new file mode 100644
index 000000000..a7bc838cd
--- /dev/null
+++ b/nodejs/node_modules/body-parser/lib/types/json.js
@@ -0,0 +1,232 @@
+/*!
+ * body-parser
+ * Copyright(c) 2014 Jonathan Ong
+ * Copyright(c) 2014-2015 Douglas Christopher Wilson
+ * MIT Licensed
+ */
+
+'use strict'
+
+/**
+ * Module dependencies.
+ * @private
+ */
+
+var bytes = require('bytes')
+var contentType = require('content-type')
+var createError = require('http-errors')
+var debug = require('debug')('body-parser:json')
+var read = require('../read')
+var typeis = require('type-is')
+
+/**
+ * Module exports.
+ */
+
+module.exports = json
+
+/**
+ * RegExp to match the first non-space in a string.
+ *
+ * Allowed whitespace is defined in RFC 7159:
+ *
+ * ws = *(
+ * %x20 / ; Space
+ * %x09 / ; Horizontal tab
+ * %x0A / ; Line feed or New line
+ * %x0D ) ; Carriage return
+ */
+
+var FIRST_CHAR_REGEXP = /^[\x20\x09\x0a\x0d]*(.)/ // eslint-disable-line no-control-regex
+
+/**
+ * Create a middleware to parse JSON bodies.
+ *
+ * @param {object} [options]
+ * @return {function}
+ * @public
+ */
+
+function json (options) {
+ var opts = options || {}
+
+ var limit = typeof opts.limit !== 'number'
+ ? bytes.parse(opts.limit || '100kb')
+ : opts.limit
+ var inflate = opts.inflate !== false
+ var reviver = opts.reviver
+ var strict = opts.strict !== false
+ var type = opts.type || 'application/json'
+ var verify = opts.verify || false
+
+ if (verify !== false && typeof verify !== 'function') {
+ throw new TypeError('option verify must be function')
+ }
+
+ // create the appropriate type checking function
+ var shouldParse = typeof type !== 'function'
+ ? typeChecker(type)
+ : type
+
+ function parse (body) {
+ if (body.length === 0) {
+ // special-case empty json body, as it's a common client-side mistake
+ // TODO: maybe make this configurable or part of "strict" option
+ return {}
+ }
+
+ if (strict) {
+ var first = firstchar(body)
+
+ if (first !== '{' && first !== '[') {
+ debug('strict violation')
+ throw createStrictSyntaxError(body, first)
+ }
+ }
+
+ try {
+ debug('parse json')
+ return JSON.parse(body, reviver)
+ } catch (e) {
+ throw normalizeJsonSyntaxError(e, {
+ stack: e.stack
+ })
+ }
+ }
+
+ return function jsonParser (req, res, next) {
+ if (req._body) {
+ debug('body already parsed')
+ next()
+ return
+ }
+
+ req.body = req.body || {}
+
+ // skip requests without bodies
+ if (!typeis.hasBody(req)) {
+ debug('skip empty body')
+ next()
+ return
+ }
+
+ debug('content-type %j', req.headers['content-type'])
+
+ // determine if request should be parsed
+ if (!shouldParse(req)) {
+ debug('skip parsing')
+ next()
+ return
+ }
+
+ // assert charset per RFC 7159 sec 8.1
+ var charset = getCharset(req) || 'utf-8'
+ if (charset.substr(0, 4) !== 'utf-') {
+ debug('invalid charset')
+ next(createError(415, 'unsupported charset "' + charset.toUpperCase() + '"', {
+ charset: charset,
+ type: 'charset.unsupported'
+ }))
+ return
+ }
+
+ // read
+ read(req, res, next, parse, debug, {
+ encoding: charset,
+ inflate: inflate,
+ limit: limit,
+ verify: verify
+ })
+ }
+}
+
+/**
+ * Create strict violation syntax error matching native error.
+ *
+ * @param {string} str
+ * @param {string} char
+ * @return {Error}
+ * @private
+ */
+
+function createStrictSyntaxError (str, char) {
+ var index = str.indexOf(char)
+ var partial = str.substring(0, index) + '#'
+
+ try {
+ JSON.parse(partial); /* istanbul ignore next */ throw new SyntaxError('strict violation')
+ } catch (e) {
+ return normalizeJsonSyntaxError(e, {
+ message: e.message.replace('#', char),
+ stack: e.stack
+ })
+ }
+}
+
+/**
+ * Get the first non-whitespace character in a string.
+ *
+ * @param {string} str
+ * @return {function}
+ * @private
+ */
+
+function firstchar (str) {
+ return FIRST_CHAR_REGEXP.exec(str)[1]
+}
+
+/**
+ * Get the charset of a request.
+ *
+ * @param {object} req
+ * @api private
+ */
+
+function getCharset (req) {
+ try {
+ return (contentType.parse(req).parameters.charset || '').toLowerCase()
+ } catch (e) {
+ return undefined
+ }
+}
+
+/**
+ * Normalize a SyntaxError for JSON.parse.
+ *
+ * @param {SyntaxError} error
+ * @param {object} obj
+ * @return {SyntaxError}
+ */
+
+function normalizeJsonSyntaxError (error, obj) {
+ var keys = Object.getOwnPropertyNames(error)
+
+ for (var i = 0; i < keys.length; i++) {
+ var key = keys[i]
+ if (key !== 'stack' && key !== 'message') {
+ delete error[key]
+ }
+ }
+
+ var props = Object.keys(obj)
+
+ for (var j = 0; j < props.length; j++) {
+ var prop = props[j]
+ error[prop] = obj[prop]
+ }
+
+ return error
+}
+
+/**
+ * Get the simple type checker.
+ *
+ * @param {string} type
+ * @return {function}
+ */
+
+function typeChecker (type) {
+ return function checkType (req) {
+ return Boolean(typeis(req, type))
+ }
+}
diff --git a/nodejs/node_modules/body-parser/lib/types/raw.js b/nodejs/node_modules/body-parser/lib/types/raw.js
new file mode 100644
index 000000000..f5d1b6747
--- /dev/null
+++ b/nodejs/node_modules/body-parser/lib/types/raw.js
@@ -0,0 +1,101 @@
+/*!
+ * body-parser
+ * Copyright(c) 2014-2015 Douglas Christopher Wilson
+ * MIT Licensed
+ */
+
+'use strict'
+
+/**
+ * Module dependencies.
+ */
+
+var bytes = require('bytes')
+var debug = require('debug')('body-parser:raw')
+var read = require('../read')
+var typeis = require('type-is')
+
+/**
+ * Module exports.
+ */
+
+module.exports = raw
+
+/**
+ * Create a middleware to parse raw bodies.
+ *
+ * @param {object} [options]
+ * @return {function}
+ * @api public
+ */
+
+function raw (options) {
+ var opts = options || {}
+
+ var inflate = opts.inflate !== false
+ var limit = typeof opts.limit !== 'number'
+ ? bytes.parse(opts.limit || '100kb')
+ : opts.limit
+ var type = opts.type || 'application/octet-stream'
+ var verify = opts.verify || false
+
+ if (verify !== false && typeof verify !== 'function') {
+ throw new TypeError('option verify must be function')
+ }
+
+ // create the appropriate type checking function
+ var shouldParse = typeof type !== 'function'
+ ? typeChecker(type)
+ : type
+
+ function parse (buf) {
+ return buf
+ }
+
+ return function rawParser (req, res, next) {
+ if (req._body) {
+ debug('body already parsed')
+ next()
+ return
+ }
+
+ req.body = req.body || {}
+
+ // skip requests without bodies
+ if (!typeis.hasBody(req)) {
+ debug('skip empty body')
+ next()
+ return
+ }
+
+ debug('content-type %j', req.headers['content-type'])
+
+ // determine if request should be parsed
+ if (!shouldParse(req)) {
+ debug('skip parsing')
+ next()
+ return
+ }
+
+ // read
+ read(req, res, next, parse, debug, {
+ encoding: null,
+ inflate: inflate,
+ limit: limit,
+ verify: verify
+ })
+ }
+}
+
+/**
+ * Get the simple type checker.
+ *
+ * @param {string} type
+ * @return {function}
+ */
+
+function typeChecker (type) {
+ return function checkType (req) {
+ return Boolean(typeis(req, type))
+ }
+}
diff --git a/nodejs/node_modules/body-parser/lib/types/text.js b/nodejs/node_modules/body-parser/lib/types/text.js
new file mode 100644
index 000000000..083a00908
--- /dev/null
+++ b/nodejs/node_modules/body-parser/lib/types/text.js
@@ -0,0 +1,121 @@
+/*!
+ * body-parser
+ * Copyright(c) 2014-2015 Douglas Christopher Wilson
+ * MIT Licensed
+ */
+
+'use strict'
+
+/**
+ * Module dependencies.
+ */
+
+var bytes = require('bytes')
+var contentType = require('content-type')
+var debug = require('debug')('body-parser:text')
+var read = require('../read')
+var typeis = require('type-is')
+
+/**
+ * Module exports.
+ */
+
+module.exports = text
+
+/**
+ * Create a middleware to parse text bodies.
+ *
+ * @param {object} [options]
+ * @return {function}
+ * @api public
+ */
+
+function text (options) {
+ var opts = options || {}
+
+ var defaultCharset = opts.defaultCharset || 'utf-8'
+ var inflate = opts.inflate !== false
+ var limit = typeof opts.limit !== 'number'
+ ? bytes.parse(opts.limit || '100kb')
+ : opts.limit
+ var type = opts.type || 'text/plain'
+ var verify = opts.verify || false
+
+ if (verify !== false && typeof verify !== 'function') {
+ throw new TypeError('option verify must be function')
+ }
+
+ // create the appropriate type checking function
+ var shouldParse = typeof type !== 'function'
+ ? typeChecker(type)
+ : type
+
+ function parse (buf) {
+ return buf
+ }
+
+ return function textParser (req, res, next) {
+ if (req._body) {
+ debug('body already parsed')
+ next()
+ return
+ }
+
+ req.body = req.body || {}
+
+ // skip requests without bodies
+ if (!typeis.hasBody(req)) {
+ debug('skip empty body')
+ next()
+ return
+ }
+
+ debug('content-type %j', req.headers['content-type'])
+
+ // determine if request should be parsed
+ if (!shouldParse(req)) {
+ debug('skip parsing')
+ next()
+ return
+ }
+
+ // get charset
+ var charset = getCharset(req) || defaultCharset
+
+ // read
+ read(req, res, next, parse, debug, {
+ encoding: charset,
+ inflate: inflate,
+ limit: limit,
+ verify: verify
+ })
+ }
+}
+
+/**
+ * Get the charset of a request.
+ *
+ * @param {object} req
+ * @api private
+ */
+
+function getCharset (req) {
+ try {
+ return (contentType.parse(req).parameters.charset || '').toLowerCase()
+ } catch (e) {
+ return undefined
+ }
+}
+
+/**
+ * Get the simple type checker.
+ *
+ * @param {string} type
+ * @return {function}
+ */
+
+function typeChecker (type) {
+ return function checkType (req) {
+ return Boolean(typeis(req, type))
+ }
+}
diff --git a/nodejs/node_modules/body-parser/lib/types/urlencoded.js b/nodejs/node_modules/body-parser/lib/types/urlencoded.js
new file mode 100644
index 000000000..5ccda2182
--- /dev/null
+++ b/nodejs/node_modules/body-parser/lib/types/urlencoded.js
@@ -0,0 +1,284 @@
+/*!
+ * body-parser
+ * Copyright(c) 2014 Jonathan Ong
+ * Copyright(c) 2014-2015 Douglas Christopher Wilson
+ * MIT Licensed
+ */
+
+'use strict'
+
+/**
+ * Module dependencies.
+ * @private
+ */
+
+var bytes = require('bytes')
+var contentType = require('content-type')
+var createError = require('http-errors')
+var debug = require('debug')('body-parser:urlencoded')
+var deprecate = require('depd')('body-parser')
+var read = require('../read')
+var typeis = require('type-is')
+
+/**
+ * Module exports.
+ */
+
+module.exports = urlencoded
+
+/**
+ * Cache of parser modules.
+ */
+
+var parsers = Object.create(null)
+
+/**
+ * Create a middleware to parse urlencoded bodies.
+ *
+ * @param {object} [options]
+ * @return {function}
+ * @public
+ */
+
+function urlencoded (options) {
+ var opts = options || {}
+
+ // notice because option default will flip in next major
+ if (opts.extended === undefined) {
+ deprecate('undefined extended: provide extended option')
+ }
+
+ var extended = opts.extended !== false
+ var inflate = opts.inflate !== false
+ var limit = typeof opts.limit !== 'number'
+ ? bytes.parse(opts.limit || '100kb')
+ : opts.limit
+ var type = opts.type || 'application/x-www-form-urlencoded'
+ var verify = opts.verify || false
+
+ if (verify !== false && typeof verify !== 'function') {
+ throw new TypeError('option verify must be function')
+ }
+
+ // create the appropriate query parser
+ var queryparse = extended
+ ? extendedparser(opts)
+ : simpleparser(opts)
+
+ // create the appropriate type checking function
+ var shouldParse = typeof type !== 'function'
+ ? typeChecker(type)
+ : type
+
+ function parse (body) {
+ return body.length
+ ? queryparse(body)
+ : {}
+ }
+
+ return function urlencodedParser (req, res, next) {
+ if (req._body) {
+ debug('body already parsed')
+ next()
+ return
+ }
+
+ req.body = req.body || {}
+
+ // skip requests without bodies
+ if (!typeis.hasBody(req)) {
+ debug('skip empty body')
+ next()
+ return
+ }
+
+ debug('content-type %j', req.headers['content-type'])
+
+ // determine if request should be parsed
+ if (!shouldParse(req)) {
+ debug('skip parsing')
+ next()
+ return
+ }
+
+ // assert charset
+ var charset = getCharset(req) || 'utf-8'
+ if (charset !== 'utf-8') {
+ debug('invalid charset')
+ next(createError(415, 'unsupported charset "' + charset.toUpperCase() + '"', {
+ charset: charset,
+ type: 'charset.unsupported'
+ }))
+ return
+ }
+
+ // read
+ read(req, res, next, parse, debug, {
+ debug: debug,
+ encoding: charset,
+ inflate: inflate,
+ limit: limit,
+ verify: verify
+ })
+ }
+}
+
+/**
+ * Get the extended query parser.
+ *
+ * @param {object} options
+ */
+
+function extendedparser (options) {
+ var parameterLimit = options.parameterLimit !== undefined
+ ? options.parameterLimit
+ : 1000
+ var parse = parser('qs')
+
+ if (isNaN(parameterLimit) || parameterLimit < 1) {
+ throw new TypeError('option parameterLimit must be a positive number')
+ }
+
+ if (isFinite(parameterLimit)) {
+ parameterLimit = parameterLimit | 0
+ }
+
+ return function queryparse (body) {
+ var paramCount = parameterCount(body, parameterLimit)
+
+ if (paramCount === undefined) {
+ debug('too many parameters')
+ throw createError(413, 'too many parameters', {
+ type: 'parameters.too.many'
+ })
+ }
+
+ var arrayLimit = Math.max(100, paramCount)
+
+ debug('parse extended urlencoding')
+ return parse(body, {
+ allowPrototypes: true,
+ arrayLimit: arrayLimit,
+ depth: Infinity,
+ parameterLimit: parameterLimit
+ })
+ }
+}
+
+/**
+ * Get the charset of a request.
+ *
+ * @param {object} req
+ * @api private
+ */
+
+function getCharset (req) {
+ try {
+ return (contentType.parse(req).parameters.charset || '').toLowerCase()
+ } catch (e) {
+ return undefined
+ }
+}
+
+/**
+ * Count the number of parameters, stopping once limit reached
+ *
+ * @param {string} body
+ * @param {number} limit
+ * @api private
+ */
+
+function parameterCount (body, limit) {
+ var count = 0
+ var index = 0
+
+ while ((index = body.indexOf('&', index)) !== -1) {
+ count++
+ index++
+
+ if (count === limit) {
+ return undefined
+ }
+ }
+
+ return count
+}
+
+/**
+ * Get parser for module name dynamically.
+ *
+ * @param {string} name
+ * @return {function}
+ * @api private
+ */
+
+function parser (name) {
+ var mod = parsers[name]
+
+ if (mod !== undefined) {
+ return mod.parse
+ }
+
+ // this uses a switch for static require analysis
+ switch (name) {
+ case 'qs':
+ mod = require('qs')
+ break
+ case 'querystring':
+ mod = require('querystring')
+ break
+ }
+
+ // store to prevent invoking require()
+ parsers[name] = mod
+
+ return mod.parse
+}
+
+/**
+ * Get the simple query parser.
+ *
+ * @param {object} options
+ */
+
+function simpleparser (options) {
+ var parameterLimit = options.parameterLimit !== undefined
+ ? options.parameterLimit
+ : 1000
+ var parse = parser('querystring')
+
+ if (isNaN(parameterLimit) || parameterLimit < 1) {
+ throw new TypeError('option parameterLimit must be a positive number')
+ }
+
+ if (isFinite(parameterLimit)) {
+ parameterLimit = parameterLimit | 0
+ }
+
+ return function queryparse (body) {
+ var paramCount = parameterCount(body, parameterLimit)
+
+ if (paramCount === undefined) {
+ debug('too many parameters')
+ throw createError(413, 'too many parameters', {
+ type: 'parameters.too.many'
+ })
+ }
+
+ debug('parse urlencoding')
+ return parse(body, undefined, undefined, {maxKeys: parameterLimit})
+ }
+}
+
+/**
+ * Get the simple type checker.
+ *
+ * @param {string} type
+ * @return {function}
+ */
+
+function typeChecker (type) {
+ return function checkType (req) {
+ return Boolean(typeis(req, type))
+ }
+}
diff --git a/nodejs/node_modules/body-parser/package.json b/nodejs/node_modules/body-parser/package.json
new file mode 100644
index 000000000..405f56094
--- /dev/null
+++ b/nodejs/node_modules/body-parser/package.json
@@ -0,0 +1,94 @@
+{
+ "_args": [
+ [
+ "body-parser@1.18.2",
+ "/Users/muammar/github/mkchromecast/nodejs"
+ ]
+ ],
+ "_from": "body-parser@1.18.2",
+ "_id": "body-parser@1.18.2",
+ "_inBundle": false,
+ "_integrity": "sha1-h2eKGdhLR9hZuDGZvVm84iKxBFQ=",
+ "_location": "/body-parser",
+ "_phantomChildren": {},
+ "_requested": {
+ "type": "version",
+ "registry": true,
+ "raw": "body-parser@1.18.2",
+ "name": "body-parser",
+ "escapedName": "body-parser",
+ "rawSpec": "1.18.2",
+ "saveSpec": null,
+ "fetchSpec": "1.18.2"
+ },
+ "_requiredBy": [
+ "/express"
+ ],
+ "_resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.2.tgz",
+ "_spec": "1.18.2",
+ "_where": "/Users/muammar/github/mkchromecast/nodejs",
+ "bugs": {
+ "url": "https://github.com/expressjs/body-parser/issues"
+ },
+ "contributors": [
+ {
+ "name": "Douglas Christopher Wilson",
+ "email": "doug@somethingdoug.com"
+ },
+ {
+ "name": "Jonathan Ong",
+ "email": "me@jongleberry.com",
+ "url": "http://jongleberry.com"
+ }
+ ],
+ "dependencies": {
+ "bytes": "3.0.0",
+ "content-type": "~1.0.4",
+ "debug": "2.6.9",
+ "depd": "~1.1.1",
+ "http-errors": "~1.6.2",
+ "iconv-lite": "0.4.19",
+ "on-finished": "~2.3.0",
+ "qs": "6.5.1",
+ "raw-body": "2.3.2",
+ "type-is": "~1.6.15"
+ },
+ "description": "Node.js body parsing middleware",
+ "devDependencies": {
+ "eslint": "3.19.0",
+ "eslint-config-standard": "10.2.1",
+ "eslint-plugin-import": "2.7.0",
+ "eslint-plugin-markdown": "1.0.0-beta.6",
+ "eslint-plugin-node": "5.1.1",
+ "eslint-plugin-promise": "3.5.0",
+ "eslint-plugin-standard": "3.0.1",
+ "istanbul": "0.4.5",
+ "methods": "1.1.2",
+ "mocha": "2.5.3",
+ "safe-buffer": "5.1.1",
+ "supertest": "1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ },
+ "files": [
+ "lib/",
+ "LICENSE",
+ "HISTORY.md",
+ "index.js"
+ ],
+ "homepage": "https://github.com/expressjs/body-parser#readme",
+ "license": "MIT",
+ "name": "body-parser",
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/expressjs/body-parser.git"
+ },
+ "scripts": {
+ "lint": "eslint --plugin markdown --ext js,md .",
+ "test": "mocha --require test/support/env --reporter spec --check-leaks --bail test/",
+ "test-cov": "istanbul cover node_modules/mocha/bin/_mocha -- --require test/support/env --reporter dot --check-leaks test/",
+ "test-travis": "istanbul cover node_modules/mocha/bin/_mocha --report lcovonly -- --require test/support/env --reporter spec --check-leaks test/"
+ },
+ "version": "1.18.2"
+}
diff --git a/nodejs/node_modules/bytes/History.md b/nodejs/node_modules/bytes/History.md
new file mode 100644
index 000000000..13d463ab1
--- /dev/null
+++ b/nodejs/node_modules/bytes/History.md
@@ -0,0 +1,82 @@
+3.0.0 / 2017-08-31
+==================
+
+ * Change "kB" to "KB" in format output
+ * Remove support for Node.js 0.6
+ * Remove support for ComponentJS
+
+2.5.0 / 2017-03-24
+==================
+
+ * Add option "unit"
+
+2.4.0 / 2016-06-01
+==================
+
+ * Add option "unitSeparator"
+
+2.3.0 / 2016-02-15
+==================
+
+ * Drop partial bytes on all parsed units
+ * Fix non-finite numbers to `.format` to return `null`
+ * Fix parsing byte string that looks like hex
+ * perf: hoist regular expressions
+
+2.2.0 / 2015-11-13
+==================
+
+ * add option "decimalPlaces"
+ * add option "fixedDecimals"
+
+2.1.0 / 2015-05-21
+==================
+
+ * add `.format` export
+ * add `.parse` export
+
+2.0.2 / 2015-05-20
+==================
+
+ * remove map recreation
+ * remove unnecessary object construction
+
+2.0.1 / 2015-05-07
+==================
+
+ * fix browserify require
+ * remove node.extend dependency
+
+2.0.0 / 2015-04-12
+==================
+
+ * add option "case"
+ * add option "thousandsSeparator"
+ * return "null" on invalid parse input
+ * support proper round-trip: bytes(bytes(num)) === num
+ * units no longer case sensitive when parsing
+
+1.0.0 / 2014-05-05
+==================
+
+ * add negative support. fixes #6
+
+0.3.0 / 2014-03-19
+==================
+
+ * added terabyte support
+
+0.2.1 / 2013-04-01
+==================
+
+ * add .component
+
+0.2.0 / 2012-10-28
+==================
+
+ * bytes(200).should.eql('200b')
+
+0.1.0 / 2012-07-04
+==================
+
+ * add bytes to string conversion [yields]
diff --git a/nodejs/node_modules/bytes/LICENSE b/nodejs/node_modules/bytes/LICENSE
new file mode 100644
index 000000000..63e95a963
--- /dev/null
+++ b/nodejs/node_modules/bytes/LICENSE
@@ -0,0 +1,23 @@
+(The MIT License)
+
+Copyright (c) 2012-2014 TJ Holowaychuk
+Copyright (c) 2015 Jed Watson
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+'Software'), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/nodejs/node_modules/bytes/Readme.md b/nodejs/node_modules/bytes/Readme.md
new file mode 100644
index 000000000..9b53745d1
--- /dev/null
+++ b/nodejs/node_modules/bytes/Readme.md
@@ -0,0 +1,125 @@
+# Bytes utility
+
+[![NPM Version][npm-image]][npm-url]
+[![NPM Downloads][downloads-image]][downloads-url]
+[![Build Status][travis-image]][travis-url]
+[![Test Coverage][coveralls-image]][coveralls-url]
+
+Utility to parse a string bytes (ex: `1TB`) to bytes (`1099511627776`) and vice-versa.
+
+## Installation
+
+This is a [Node.js](https://nodejs.org/en/) module available through the
+[npm registry](https://www.npmjs.com/). Installation is done using the
+[`npm install` command](https://docs.npmjs.com/getting-started/installing-npm-packages-locally):
+
+```bash
+$ npm install bytes
+```
+
+## Usage
+
+```js
+var bytes = require('bytes');
+```
+
+#### bytes.format(number value, [options]): string|null
+
+Format the given value in bytes into a string. If the value is negative, it is kept as such. If it is a float, it is
+ rounded.
+
+**Arguments**
+
+| Name | Type | Description |
+|---------|----------|--------------------|
+| value | `number` | Value in bytes |
+| options | `Object` | Conversion options |
+
+**Options**
+
+| Property | Type | Description |
+|-------------------|--------|-----------------------------------------------------------------------------------------|
+| decimalPlaces | `number`|`null` | Maximum number of decimal places to include in output. Default value to `2`. |
+| fixedDecimals | `boolean`|`null` | Whether to always display the maximum number of decimal places. Default value to `false` |
+| thousandsSeparator | `string`|`null` | Example of values: `' '`, `','` and `.`... Default value to `''`. |
+| unit | `string`|`null` | The unit in which the result will be returned (B/KB/MB/GB/TB). Default value to `''` (which means auto detect). |
+| unitSeparator | `string`|`null` | Separator to use between number and unit. Default value to `''`. |
+
+**Returns**
+
+| Name | Type | Description |
+|---------|------------------|-------------------------------------------------|
+| results | `string`|`null` | Return null upon error. String value otherwise. |
+
+**Example**
+
+```js
+bytes(1024);
+// output: '1KB'
+
+bytes(1000);
+// output: '1000B'
+
+bytes(1000, {thousandsSeparator: ' '});
+// output: '1 000B'
+
+bytes(1024 * 1.7, {decimalPlaces: 0});
+// output: '2KB'
+
+bytes(1024, {unitSeparator: ' '});
+// output: '1 KB'
+
+```
+
+#### bytes.parse(string|number value): number|null
+
+Parse the string value into an integer in bytes. If no unit is given, or `value`
+is a number, it is assumed the value is in bytes.
+
+Supported units and abbreviations are as follows and are case-insensitive:
+
+ * `b` for bytes
+ * `kb` for kilobytes
+ * `mb` for megabytes
+ * `gb` for gigabytes
+ * `tb` for terabytes
+
+The units are in powers of two, not ten. This means 1kb = 1024b according to this parser.
+
+**Arguments**
+
+| Name | Type | Description |
+|---------------|--------|--------------------|
+| value | `string`|`number` | String to parse, or number in bytes. |
+
+**Returns**
+
+| Name | Type | Description |
+|---------|-------------|-------------------------|
+| results | `number`|`null` | Return null upon error. Value in bytes otherwise. |
+
+**Example**
+
+```js
+bytes('1KB');
+// output: 1024
+
+bytes('1024');
+// output: 1024
+
+bytes(1024);
+// output: 1024
+```
+
+## License
+
+[MIT](LICENSE)
+
+[downloads-image]: https://img.shields.io/npm/dm/bytes.svg
+[downloads-url]: https://npmjs.org/package/bytes
+[npm-image]: https://img.shields.io/npm/v/bytes.svg
+[npm-url]: https://npmjs.org/package/bytes
+[travis-image]: https://img.shields.io/travis/visionmedia/bytes.js/master.svg
+[travis-url]: https://travis-ci.org/visionmedia/bytes.js
+[coveralls-image]: https://img.shields.io/coveralls/visionmedia/bytes.js/master.svg
+[coveralls-url]: https://coveralls.io/r/visionmedia/bytes.js?branch=master
diff --git a/nodejs/node_modules/bytes/index.js b/nodejs/node_modules/bytes/index.js
new file mode 100644
index 000000000..1e39afd1d
--- /dev/null
+++ b/nodejs/node_modules/bytes/index.js
@@ -0,0 +1,159 @@
+/*!
+ * bytes
+ * Copyright(c) 2012-2014 TJ Holowaychuk
+ * Copyright(c) 2015 Jed Watson
+ * MIT Licensed
+ */
+
+'use strict';
+
+/**
+ * Module exports.
+ * @public
+ */
+
+module.exports = bytes;
+module.exports.format = format;
+module.exports.parse = parse;
+
+/**
+ * Module variables.
+ * @private
+ */
+
+var formatThousandsRegExp = /\B(?=(\d{3})+(?!\d))/g;
+
+var formatDecimalsRegExp = /(?:\.0*|(\.[^0]+)0+)$/;
+
+var map = {
+ b: 1,
+ kb: 1 << 10,
+ mb: 1 << 20,
+ gb: 1 << 30,
+ tb: ((1 << 30) * 1024)
+};
+
+var parseRegExp = /^((-|\+)?(\d+(?:\.\d+)?)) *(kb|mb|gb|tb)$/i;
+
+/**
+ * Convert the given value in bytes into a string or parse to string to an integer in bytes.
+ *
+ * @param {string|number} value
+ * @param {{
+ * case: [string],
+ * decimalPlaces: [number]
+ * fixedDecimals: [boolean]
+ * thousandsSeparator: [string]
+ * unitSeparator: [string]
+ * }} [options] bytes options.
+ *
+ * @returns {string|number|null}
+ */
+
+function bytes(value, options) {
+ if (typeof value === 'string') {
+ return parse(value);
+ }
+
+ if (typeof value === 'number') {
+ return format(value, options);
+ }
+
+ return null;
+}
+
+/**
+ * Format the given value in bytes into a string.
+ *
+ * If the value is negative, it is kept as such. If it is a float,
+ * it is rounded.
+ *
+ * @param {number} value
+ * @param {object} [options]
+ * @param {number} [options.decimalPlaces=2]
+ * @param {number} [options.fixedDecimals=false]
+ * @param {string} [options.thousandsSeparator=]
+ * @param {string} [options.unit=]
+ * @param {string} [options.unitSeparator=]
+ *
+ * @returns {string|null}
+ * @public
+ */
+
+function format(value, options) {
+ if (!Number.isFinite(value)) {
+ return null;
+ }
+
+ var mag = Math.abs(value);
+ var thousandsSeparator = (options && options.thousandsSeparator) || '';
+ var unitSeparator = (options && options.unitSeparator) || '';
+ var decimalPlaces = (options && options.decimalPlaces !== undefined) ? options.decimalPlaces : 2;
+ var fixedDecimals = Boolean(options && options.fixedDecimals);
+ var unit = (options && options.unit) || '';
+
+ if (!unit || !map[unit.toLowerCase()]) {
+ if (mag >= map.tb) {
+ unit = 'TB';
+ } else if (mag >= map.gb) {
+ unit = 'GB';
+ } else if (mag >= map.mb) {
+ unit = 'MB';
+ } else if (mag >= map.kb) {
+ unit = 'KB';
+ } else {
+ unit = 'B';
+ }
+ }
+
+ var val = value / map[unit.toLowerCase()];
+ var str = val.toFixed(decimalPlaces);
+
+ if (!fixedDecimals) {
+ str = str.replace(formatDecimalsRegExp, '$1');
+ }
+
+ if (thousandsSeparator) {
+ str = str.replace(formatThousandsRegExp, thousandsSeparator);
+ }
+
+ return str + unitSeparator + unit;
+}
+
+/**
+ * Parse the string value into an integer in bytes.
+ *
+ * If no unit is given, it is assumed the value is in bytes.
+ *
+ * @param {number|string} val
+ *
+ * @returns {number|null}
+ * @public
+ */
+
+function parse(val) {
+ if (typeof val === 'number' && !isNaN(val)) {
+ return val;
+ }
+
+ if (typeof val !== 'string') {
+ return null;
+ }
+
+ // Test if the string passed is valid
+ var results = parseRegExp.exec(val);
+ var floatValue;
+ var unit = 'b';
+
+ if (!results) {
+ // Nothing could be extracted from the given string
+ floatValue = parseInt(val, 10);
+ unit = 'b'
+ } else {
+ // Retrieve the value and the unit
+ floatValue = parseFloat(results[1]);
+ unit = results[4].toLowerCase();
+ }
+
+ return Math.floor(map[unit] * floatValue);
+}
diff --git a/nodejs/node_modules/bytes/package.json b/nodejs/node_modules/bytes/package.json
new file mode 100644
index 000000000..982bbbf32
--- /dev/null
+++ b/nodejs/node_modules/bytes/package.json
@@ -0,0 +1,85 @@
+{
+ "_args": [
+ [
+ "bytes@3.0.0",
+ "/Users/muammar/github/mkchromecast/nodejs"
+ ]
+ ],
+ "_from": "bytes@3.0.0",
+ "_id": "bytes@3.0.0",
+ "_inBundle": false,
+ "_integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=",
+ "_location": "/bytes",
+ "_phantomChildren": {},
+ "_requested": {
+ "type": "version",
+ "registry": true,
+ "raw": "bytes@3.0.0",
+ "name": "bytes",
+ "escapedName": "bytes",
+ "rawSpec": "3.0.0",
+ "saveSpec": null,
+ "fetchSpec": "3.0.0"
+ },
+ "_requiredBy": [
+ "/body-parser",
+ "/raw-body"
+ ],
+ "_resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz",
+ "_spec": "3.0.0",
+ "_where": "/Users/muammar/github/mkchromecast/nodejs",
+ "author": {
+ "name": "TJ Holowaychuk",
+ "email": "tj@vision-media.ca",
+ "url": "http://tjholowaychuk.com"
+ },
+ "bugs": {
+ "url": "https://github.com/visionmedia/bytes.js/issues"
+ },
+ "contributors": [
+ {
+ "name": "Jed Watson",
+ "email": "jed.watson@me.com"
+ },
+ {
+ "name": "Théo FIDRY",
+ "email": "theo.fidry@gmail.com"
+ }
+ ],
+ "description": "Utility to parse a string bytes to bytes and vice-versa",
+ "devDependencies": {
+ "mocha": "2.5.3",
+ "nyc": "10.3.2"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ },
+ "files": [
+ "History.md",
+ "LICENSE",
+ "Readme.md",
+ "index.js"
+ ],
+ "homepage": "https://github.com/visionmedia/bytes.js#readme",
+ "keywords": [
+ "byte",
+ "bytes",
+ "utility",
+ "parse",
+ "parser",
+ "convert",
+ "converter"
+ ],
+ "license": "MIT",
+ "name": "bytes",
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/visionmedia/bytes.js.git"
+ },
+ "scripts": {
+ "test": "mocha --check-leaks --reporter spec",
+ "test-ci": "nyc --reporter=text npm test",
+ "test-cov": "nyc --reporter=html --reporter=text npm test"
+ },
+ "version": "3.0.0"
+}
diff --git a/nodejs/node_modules/chalk/package.json b/nodejs/node_modules/chalk/package.json
index a58b7cec4..9aa959e79 100644
--- a/nodejs/node_modules/chalk/package.json
+++ b/nodejs/node_modules/chalk/package.json
@@ -1,45 +1,32 @@
{
"_args": [
[
- {
- "raw": "chalk@^0.5.1",
- "scope": null,
- "escapedName": "chalk",
- "name": "chalk",
- "rawSpec": "^0.5.1",
- "spec": ">=0.5.1 <0.6.0",
- "type": "range"
- },
- "/Users/muammar/github/mkchromecast/nodejs/node_modules/webcast-osx-audio"
+ "chalk@0.5.1",
+ "/Users/muammar/github/mkchromecast/nodejs"
]
],
- "_from": "chalk@>=0.5.1 <0.6.0",
+ "_from": "chalk@0.5.1",
"_id": "chalk@0.5.1",
- "_inCache": true,
+ "_inBundle": false,
+ "_integrity": "sha1-Zjs6ZItotV0EaQ1JFnqoN4WPIXQ=",
"_location": "/chalk",
- "_npmUser": {
- "name": "jbnicolai",
- "email": "jappelman@xebia.com"
- },
- "_npmVersion": "1.4.14",
"_phantomChildren": {},
"_requested": {
- "raw": "chalk@^0.5.1",
- "scope": null,
- "escapedName": "chalk",
+ "type": "version",
+ "registry": true,
+ "raw": "chalk@0.5.1",
"name": "chalk",
- "rawSpec": "^0.5.1",
- "spec": ">=0.5.1 <0.6.0",
- "type": "range"
+ "escapedName": "chalk",
+ "rawSpec": "0.5.1",
+ "saveSpec": null,
+ "fetchSpec": "0.5.1"
},
"_requiredBy": [
"/webcast-osx-audio"
],
"_resolved": "https://registry.npmjs.org/chalk/-/chalk-0.5.1.tgz",
- "_shasum": "663b3a648b68b55d04690d49167aa837858f2174",
- "_shrinkwrap": null,
- "_spec": "chalk@^0.5.1",
- "_where": "/Users/muammar/github/mkchromecast/nodejs/node_modules/webcast-osx-audio",
+ "_spec": "0.5.1",
+ "_where": "/Users/muammar/github/mkchromecast/nodejs",
"bugs": {
"url": "https://github.com/sindresorhus/chalk/issues"
},
@@ -55,19 +42,13 @@
"matcha": "^0.5.0",
"mocha": "*"
},
- "directories": {},
- "dist": {
- "shasum": "663b3a648b68b55d04690d49167aa837858f2174",
- "tarball": "https://registry.npmjs.org/chalk/-/chalk-0.5.1.tgz"
- },
"engines": {
"node": ">=0.10.0"
},
"files": [
"index.js"
],
- "gitHead": "994758f01293f1fdcf63282e9917cb9f2cfbdaac",
- "homepage": "https://github.com/sindresorhus/chalk",
+ "homepage": "https://github.com/sindresorhus/chalk#readme",
"keywords": [
"color",
"colour",
@@ -92,20 +73,19 @@
"license": "MIT",
"maintainers": [
{
- "name": "sindresorhus",
- "email": "sindresorhus@gmail.com"
+ "name": "Sindre Sorhus",
+ "email": "sindresorhus@gmail.com",
+ "url": "http://sindresorhus.com"
},
{
- "name": "jbnicolai",
- "email": "jappelman@xebia.com"
+ "name": "Joshua Appelman",
+ "email": "joshua@jbna.nl"
}
],
"name": "chalk",
- "optionalDependencies": {},
- "readme": "ERROR: No README data found!",
"repository": {
"type": "git",
- "url": "git://github.com/sindresorhus/chalk.git"
+ "url": "git+https://github.com/sindresorhus/chalk.git"
},
"scripts": {
"bench": "matcha benchmark.js",
diff --git a/nodejs/node_modules/content-disposition/package.json b/nodejs/node_modules/content-disposition/package.json
index 207c068fa..fca050963 100644
--- a/nodejs/node_modules/content-disposition/package.json
+++ b/nodejs/node_modules/content-disposition/package.json
@@ -1,50 +1,32 @@
{
"_args": [
[
- {
- "raw": "content-disposition@0.5.2",
- "scope": null,
- "escapedName": "content-disposition",
- "name": "content-disposition",
- "rawSpec": "0.5.2",
- "spec": "0.5.2",
- "type": "version"
- },
- "/Users/muammar/github/mkchromecast/nodejs/node_modules/express"
+ "content-disposition@0.5.2",
+ "/Users/muammar/github/mkchromecast/nodejs"
]
],
"_from": "content-disposition@0.5.2",
"_id": "content-disposition@0.5.2",
- "_inCache": true,
+ "_inBundle": false,
+ "_integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=",
"_location": "/content-disposition",
- "_nodeVersion": "4.6.0",
- "_npmOperationalInternal": {
- "host": "packages-18-east.internal.npmjs.com",
- "tmp": "tmp/content-disposition-0.5.2.tgz_1481246224565_0.35659545403905213"
- },
- "_npmUser": {
- "name": "dougwilson",
- "email": "doug@somethingdoug.com"
- },
- "_npmVersion": "2.15.9",
"_phantomChildren": {},
"_requested": {
+ "type": "version",
+ "registry": true,
"raw": "content-disposition@0.5.2",
- "scope": null,
- "escapedName": "content-disposition",
"name": "content-disposition",
+ "escapedName": "content-disposition",
"rawSpec": "0.5.2",
- "spec": "0.5.2",
- "type": "version"
+ "saveSpec": null,
+ "fetchSpec": "0.5.2"
},
"_requiredBy": [
"/express"
],
"_resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz",
- "_shasum": "0cf68bb9ddf5f2be7961c3a85178cb85dba78cb4",
- "_shrinkwrap": null,
- "_spec": "content-disposition@0.5.2",
- "_where": "/Users/muammar/github/mkchromecast/nodejs/node_modules/express",
+ "_spec": "0.5.2",
+ "_where": "/Users/muammar/github/mkchromecast/nodejs",
"bugs": {
"url": "https://github.com/jshttp/content-disposition/issues"
},
@@ -54,7 +36,6 @@
"email": "doug@somethingdoug.com"
}
],
- "dependencies": {},
"description": "Create and parse Content-Disposition header",
"devDependencies": {
"eslint": "3.11.1",
@@ -64,11 +45,6 @@
"istanbul": "0.4.5",
"mocha": "1.21.5"
},
- "directories": {},
- "dist": {
- "shasum": "0cf68bb9ddf5f2be7961c3a85178cb85dba78cb4",
- "tarball": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz"
- },
"engines": {
"node": ">= 0.6"
},
@@ -78,7 +54,6 @@
"README.md",
"index.js"
],
- "gitHead": "2a08417377cf55678c9f870b305f3c6c088920f3",
"homepage": "https://github.com/jshttp/content-disposition#readme",
"keywords": [
"content-disposition",
@@ -87,15 +62,7 @@
"res"
],
"license": "MIT",
- "maintainers": [
- {
- "name": "dougwilson",
- "email": "doug@somethingdoug.com"
- }
- ],
"name": "content-disposition",
- "optionalDependencies": {},
- "readme": "ERROR: No README data found!",
"repository": {
"type": "git",
"url": "git+https://github.com/jshttp/content-disposition.git"
diff --git a/nodejs/node_modules/content-type/HISTORY.md b/nodejs/node_modules/content-type/HISTORY.md
index 01652ff46..8f5cb7030 100644
--- a/nodejs/node_modules/content-type/HISTORY.md
+++ b/nodejs/node_modules/content-type/HISTORY.md
@@ -1,3 +1,13 @@
+1.0.4 / 2017-09-11
+==================
+
+ * perf: skip parameter parsing when no parameters
+
+1.0.3 / 2017-09-10
+==================
+
+ * perf: remove argument reassignment
+
1.0.2 / 2016-05-09
==================
diff --git a/nodejs/node_modules/content-type/index.js b/nodejs/node_modules/content-type/index.js
index 61ba6b5a2..6ce03f208 100644
--- a/nodejs/node_modules/content-type/index.js
+++ b/nodejs/node_modules/content-type/index.js
@@ -20,9 +20,9 @@
* obs-text = %x80-FF
* quoted-pair = "\" ( HTAB / SP / VCHAR / obs-text )
*/
-var paramRegExp = /; *([!#$%&'\*\+\-\.\^_`\|~0-9A-Za-z]+) *= *("(?:[\u000b\u0020\u0021\u0023-\u005b\u005d-\u007e\u0080-\u00ff]|\\[\u000b\u0020-\u00ff])*"|[!#$%&'\*\+\-\.\^_`\|~0-9A-Za-z]+) */g
-var textRegExp = /^[\u000b\u0020-\u007e\u0080-\u00ff]+$/
-var tokenRegExp = /^[!#$%&'\*\+\-\.\^_`\|~0-9A-Za-z]+$/
+var PARAM_REGEXP = /; *([!#$%&'*+.^_`|~0-9A-Za-z-]+) *= *("(?:[\u000b\u0020\u0021\u0023-\u005b\u005d-\u007e\u0080-\u00ff]|\\[\u000b\u0020-\u00ff])*"|[!#$%&'*+.^_`|~0-9A-Za-z-]+) */g
+var TEXT_REGEXP = /^[\u000b\u0020-\u007e\u0080-\u00ff]+$/
+var TOKEN_REGEXP = /^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/
/**
* RegExp to match quoted-pair in RFC 7230 sec 3.2.6
@@ -30,21 +30,21 @@ var tokenRegExp = /^[!#$%&'\*\+\-\.\^_`\|~0-9A-Za-z]+$/
* quoted-pair = "\" ( HTAB / SP / VCHAR / obs-text )
* obs-text = %x80-FF
*/
-var qescRegExp = /\\([\u000b\u0020-\u00ff])/g
+var QESC_REGEXP = /\\([\u000b\u0020-\u00ff])/g
/**
* RegExp to match chars that must be quoted-pair in RFC 7230 sec 3.2.6
*/
-var quoteRegExp = /([\\"])/g
+var QUOTE_REGEXP = /([\\"])/g
/**
- * RegExp to match type in RFC 6838
+ * RegExp to match type in RFC 7231 sec 3.1.1.1
*
* media-type = type "/" subtype
* type = token
* subtype = token
*/
-var typeRegExp = /^[!#$%&'\*\+\-\.\^_`\|~0-9A-Za-z]+\/[!#$%&'\*\+\-\.\^_`\|~0-9A-Za-z]+$/
+var TYPE_REGEXP = /^[!#$%&'*+.^_`|~0-9A-Za-z-]+\/[!#$%&'*+.^_`|~0-9A-Za-z-]+$/
/**
* Module exports.
@@ -62,7 +62,7 @@ exports.parse = parse
* @public
*/
-function format(obj) {
+function format (obj) {
if (!obj || typeof obj !== 'object') {
throw new TypeError('argument obj is required')
}
@@ -70,7 +70,7 @@ function format(obj) {
var parameters = obj.parameters
var type = obj.type
- if (!type || !typeRegExp.test(type)) {
+ if (!type || !TYPE_REGEXP.test(type)) {
throw new TypeError('invalid type')
}
@@ -84,7 +84,7 @@ function format(obj) {
for (var i = 0; i < params.length; i++) {
param = params[i]
- if (!tokenRegExp.test(param)) {
+ if (!TOKEN_REGEXP.test(param)) {
throw new TypeError('invalid parameter name')
}
@@ -103,61 +103,61 @@ function format(obj) {
* @public
*/
-function parse(string) {
+function parse (string) {
if (!string) {
throw new TypeError('argument string is required')
}
- if (typeof string === 'object') {
- // support req/res-like objects as argument
- string = getcontenttype(string)
+ // support req/res-like objects as argument
+ var header = typeof string === 'object'
+ ? getcontenttype(string)
+ : string
- if (typeof string !== 'string') {
- throw new TypeError('content-type header is missing from object');
- }
- }
-
- if (typeof string !== 'string') {
+ if (typeof header !== 'string') {
throw new TypeError('argument string is required to be a string')
}
- var index = string.indexOf(';')
+ var index = header.indexOf(';')
var type = index !== -1
- ? string.substr(0, index).trim()
- : string.trim()
+ ? header.substr(0, index).trim()
+ : header.trim()
- if (!typeRegExp.test(type)) {
+ if (!TYPE_REGEXP.test(type)) {
throw new TypeError('invalid media type')
}
- var key
- var match
var obj = new ContentType(type.toLowerCase())
- var value
- paramRegExp.lastIndex = index
+ // parse parameters
+ if (index !== -1) {
+ var key
+ var match
+ var value
- while (match = paramRegExp.exec(string)) {
- if (match.index !== index) {
- throw new TypeError('invalid parameter format')
- }
+ PARAM_REGEXP.lastIndex = index
- index += match[0].length
- key = match[1].toLowerCase()
- value = match[2]
+ while ((match = PARAM_REGEXP.exec(header))) {
+ if (match.index !== index) {
+ throw new TypeError('invalid parameter format')
+ }
- if (value[0] === '"') {
- // remove quotes and escapes
- value = value
- .substr(1, value.length - 2)
- .replace(qescRegExp, '$1')
- }
+ index += match[0].length
+ key = match[1].toLowerCase()
+ value = match[2]
- obj.parameters[key] = value
- }
+ if (value[0] === '"') {
+ // remove quotes and escapes
+ value = value
+ .substr(1, value.length - 2)
+ .replace(QESC_REGEXP, '$1')
+ }
- if (index !== -1 && index !== string.length) {
- throw new TypeError('invalid parameter format')
+ obj.parameters[key] = value
+ }
+
+ if (index !== header.length) {
+ throw new TypeError('invalid parameter format')
+ }
}
return obj
@@ -171,16 +171,22 @@ function parse(string) {
* @private
*/
-function getcontenttype(obj) {
+function getcontenttype (obj) {
+ var header
+
if (typeof obj.getHeader === 'function') {
// res-like
- return obj.getHeader('content-type')
+ header = obj.getHeader('content-type')
+ } else if (typeof obj.headers === 'object') {
+ // req-like
+ header = obj.headers && obj.headers['content-type']
}
- if (typeof obj.headers === 'object') {
- // req-like
- return obj.headers && obj.headers['content-type']
+ if (typeof header !== 'string') {
+ throw new TypeError('content-type header is missing from object')
}
+
+ return header
}
/**
@@ -191,26 +197,26 @@ function getcontenttype(obj) {
* @private
*/
-function qstring(val) {
+function qstring (val) {
var str = String(val)
// no need to quote tokens
- if (tokenRegExp.test(str)) {
+ if (TOKEN_REGEXP.test(str)) {
return str
}
- if (str.length > 0 && !textRegExp.test(str)) {
+ if (str.length > 0 && !TEXT_REGEXP.test(str)) {
throw new TypeError('invalid parameter value')
}
- return '"' + str.replace(quoteRegExp, '\\$1') + '"'
+ return '"' + str.replace(QUOTE_REGEXP, '\\$1') + '"'
}
/**
* Class to represent a content type.
* @private
*/
-function ContentType(type) {
+function ContentType (type) {
this.parameters = Object.create(null)
this.type = type
}
diff --git a/nodejs/node_modules/content-type/package.json b/nodejs/node_modules/content-type/package.json
index a61596e4d..a9a4d5dc2 100644
--- a/nodejs/node_modules/content-type/package.json
+++ b/nodejs/node_modules/content-type/package.json
@@ -1,50 +1,33 @@
{
"_args": [
[
- {
- "raw": "content-type@~1.0.2",
- "scope": null,
- "escapedName": "content-type",
- "name": "content-type",
- "rawSpec": "~1.0.2",
- "spec": ">=1.0.2 <1.1.0",
- "type": "range"
- },
- "/Users/muammar/github/mkchromecast/nodejs/node_modules/express"
+ "content-type@1.0.4",
+ "/Users/muammar/github/mkchromecast/nodejs"
]
],
- "_from": "content-type@>=1.0.2 <1.1.0",
- "_id": "content-type@1.0.2",
- "_inCache": true,
+ "_from": "content-type@1.0.4",
+ "_id": "content-type@1.0.4",
+ "_inBundle": false,
+ "_integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==",
"_location": "/content-type",
- "_nodeVersion": "4.4.3",
- "_npmOperationalInternal": {
- "host": "packages-12-west.internal.npmjs.com",
- "tmp": "tmp/content-type-1.0.2.tgz_1462852785748_0.5491233412176371"
- },
- "_npmUser": {
- "name": "dougwilson",
- "email": "doug@somethingdoug.com"
- },
- "_npmVersion": "2.15.1",
"_phantomChildren": {},
"_requested": {
- "raw": "content-type@~1.0.2",
- "scope": null,
- "escapedName": "content-type",
+ "type": "version",
+ "registry": true,
+ "raw": "content-type@1.0.4",
"name": "content-type",
- "rawSpec": "~1.0.2",
- "spec": ">=1.0.2 <1.1.0",
- "type": "range"
+ "escapedName": "content-type",
+ "rawSpec": "1.0.4",
+ "saveSpec": null,
+ "fetchSpec": "1.0.4"
},
"_requiredBy": [
+ "/body-parser",
"/express"
],
- "_resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.2.tgz",
- "_shasum": "b7d113aee7a8dd27bd21133c4dc2529df1721eed",
- "_shrinkwrap": null,
- "_spec": "content-type@~1.0.2",
- "_where": "/Users/muammar/github/mkchromecast/nodejs/node_modules/express",
+ "_resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
+ "_spec": "1.0.4",
+ "_where": "/Users/muammar/github/mkchromecast/nodejs",
"author": {
"name": "Douglas Christopher Wilson",
"email": "doug@somethingdoug.com"
@@ -52,17 +35,17 @@
"bugs": {
"url": "https://github.com/jshttp/content-type/issues"
},
- "dependencies": {},
"description": "Create and parse HTTP Content-Type header",
"devDependencies": {
- "istanbul": "0.4.3",
+ "eslint": "3.19.0",
+ "eslint-config-standard": "10.2.1",
+ "eslint-plugin-import": "2.7.0",
+ "eslint-plugin-node": "5.1.1",
+ "eslint-plugin-promise": "3.5.0",
+ "eslint-plugin-standard": "3.0.1",
+ "istanbul": "0.4.5",
"mocha": "~1.21.5"
},
- "directories": {},
- "dist": {
- "shasum": "b7d113aee7a8dd27bd21133c4dc2529df1721eed",
- "tarball": "https://registry.npmjs.org/content-type/-/content-type-1.0.2.tgz"
- },
"engines": {
"node": ">= 0.6"
},
@@ -72,7 +55,6 @@
"README.md",
"index.js"
],
- "gitHead": "8118763adfbbac80cf1254191889330aec8b8be7",
"homepage": "https://github.com/jshttp/content-type#readme",
"keywords": [
"content-type",
@@ -82,23 +64,16 @@
"rfc7231"
],
"license": "MIT",
- "maintainers": [
- {
- "name": "dougwilson",
- "email": "doug@somethingdoug.com"
- }
- ],
"name": "content-type",
- "optionalDependencies": {},
- "readme": "ERROR: No README data found!",
"repository": {
"type": "git",
"url": "git+https://github.com/jshttp/content-type.git"
},
"scripts": {
+ "lint": "eslint .",
"test": "mocha --reporter spec --check-leaks --bail test/",
"test-ci": "istanbul cover node_modules/mocha/bin/_mocha --report lcovonly -- --reporter spec --check-leaks test/",
"test-cov": "istanbul cover node_modules/mocha/bin/_mocha -- --reporter dot --check-leaks test/"
},
- "version": "1.0.2"
+ "version": "1.0.4"
}
diff --git a/nodejs/node_modules/cookie-signature/package.json b/nodejs/node_modules/cookie-signature/package.json
index cf83590f4..a76876e79 100644
--- a/nodejs/node_modules/cookie-signature/package.json
+++ b/nodejs/node_modules/cookie-signature/package.json
@@ -1,46 +1,32 @@
{
"_args": [
[
- {
- "raw": "cookie-signature@1.0.6",
- "scope": null,
- "escapedName": "cookie-signature",
- "name": "cookie-signature",
- "rawSpec": "1.0.6",
- "spec": "1.0.6",
- "type": "version"
- },
- "/Users/muammar/github/mkchromecast/nodejs/node_modules/express"
+ "cookie-signature@1.0.6",
+ "/Users/muammar/github/mkchromecast/nodejs"
]
],
"_from": "cookie-signature@1.0.6",
"_id": "cookie-signature@1.0.6",
- "_inCache": true,
+ "_inBundle": false,
+ "_integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=",
"_location": "/cookie-signature",
- "_nodeVersion": "0.10.36",
- "_npmUser": {
- "name": "natevw",
- "email": "natevw@yahoo.com"
- },
- "_npmVersion": "2.3.0",
"_phantomChildren": {},
"_requested": {
+ "type": "version",
+ "registry": true,
"raw": "cookie-signature@1.0.6",
- "scope": null,
- "escapedName": "cookie-signature",
"name": "cookie-signature",
+ "escapedName": "cookie-signature",
"rawSpec": "1.0.6",
- "spec": "1.0.6",
- "type": "version"
+ "saveSpec": null,
+ "fetchSpec": "1.0.6"
},
"_requiredBy": [
"/express"
],
"_resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
- "_shasum": "e303a882b342cc3ee8ca513a79999734dab3ae2c",
- "_shrinkwrap": null,
- "_spec": "cookie-signature@1.0.6",
- "_where": "/Users/muammar/github/mkchromecast/nodejs/node_modules/express",
+ "_spec": "1.0.6",
+ "_where": "/Users/muammar/github/mkchromecast/nodejs",
"author": {
"name": "TJ Holowaychuk",
"email": "tj@learnboost.com"
@@ -54,13 +40,7 @@
"mocha": "*",
"should": "*"
},
- "directories": {},
- "dist": {
- "shasum": "e303a882b342cc3ee8ca513a79999734dab3ae2c",
- "tarball": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz"
- },
- "gitHead": "391b56cf44d88c493491b7e3fc53208cfb976d2a",
- "homepage": "https://github.com/visionmedia/node-cookie-signature",
+ "homepage": "https://github.com/visionmedia/node-cookie-signature#readme",
"keywords": [
"cookie",
"sign",
@@ -68,19 +48,7 @@
],
"license": "MIT",
"main": "index",
- "maintainers": [
- {
- "name": "tjholowaychuk",
- "email": "tj@vision-media.ca"
- },
- {
- "name": "natevw",
- "email": "natevw@yahoo.com"
- }
- ],
"name": "cookie-signature",
- "optionalDependencies": {},
- "readme": "ERROR: No README data found!",
"repository": {
"type": "git",
"url": "git+https://github.com/visionmedia/node-cookie-signature.git"
diff --git a/nodejs/node_modules/cookie/package.json b/nodejs/node_modules/cookie/package.json
index 7790922fa..a9b794c72 100644
--- a/nodejs/node_modules/cookie/package.json
+++ b/nodejs/node_modules/cookie/package.json
@@ -1,49 +1,32 @@
{
"_args": [
[
- {
- "raw": "cookie@0.3.1",
- "scope": null,
- "escapedName": "cookie",
- "name": "cookie",
- "rawSpec": "0.3.1",
- "spec": "0.3.1",
- "type": "version"
- },
- "/Users/muammar/github/mkchromecast/nodejs/node_modules/express"
+ "cookie@0.3.1",
+ "/Users/muammar/github/mkchromecast/nodejs"
]
],
"_from": "cookie@0.3.1",
"_id": "cookie@0.3.1",
- "_inCache": true,
+ "_inBundle": false,
+ "_integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=",
"_location": "/cookie",
- "_npmOperationalInternal": {
- "host": "packages-12-west.internal.npmjs.com",
- "tmp": "tmp/cookie-0.3.1.tgz_1464323556714_0.6435900838114321"
- },
- "_npmUser": {
- "name": "dougwilson",
- "email": "doug@somethingdoug.com"
- },
- "_npmVersion": "1.4.28",
"_phantomChildren": {},
"_requested": {
+ "type": "version",
+ "registry": true,
"raw": "cookie@0.3.1",
- "scope": null,
- "escapedName": "cookie",
"name": "cookie",
+ "escapedName": "cookie",
"rawSpec": "0.3.1",
- "spec": "0.3.1",
- "type": "version"
+ "saveSpec": null,
+ "fetchSpec": "0.3.1"
},
"_requiredBy": [
"/express"
],
"_resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz",
- "_shasum": "e7e0a1f9ef43b4c8ba925c5c5a96e806d16873bb",
- "_shrinkwrap": null,
- "_spec": "cookie@0.3.1",
- "_where": "/Users/muammar/github/mkchromecast/nodejs/node_modules/express",
+ "_spec": "0.3.1",
+ "_where": "/Users/muammar/github/mkchromecast/nodejs",
"author": {
"name": "Roman Shtylman",
"email": "shtylman@gmail.com"
@@ -57,17 +40,11 @@
"email": "doug@somethingdoug.com"
}
],
- "dependencies": {},
"description": "HTTP server cookie parsing and serialization",
"devDependencies": {
"istanbul": "0.4.3",
"mocha": "1.21.5"
},
- "directories": {},
- "dist": {
- "shasum": "e7e0a1f9ef43b4c8ba925c5c5a96e806d16873bb",
- "tarball": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz"
- },
"engines": {
"node": ">= 0.6"
},
@@ -77,22 +54,13 @@
"README.md",
"index.js"
],
- "gitHead": "e3c77d497d66c8b8d4b677b8954c1b192a09f0b3",
- "homepage": "https://github.com/jshttp/cookie",
+ "homepage": "https://github.com/jshttp/cookie#readme",
"keywords": [
"cookie",
"cookies"
],
"license": "MIT",
- "maintainers": [
- {
- "name": "dougwilson",
- "email": "doug@somethingdoug.com"
- }
- ],
"name": "cookie",
- "optionalDependencies": {},
- "readme": "ERROR: No README data found!",
"repository": {
"type": "git",
"url": "git+https://github.com/jshttp/cookie.git"
diff --git a/nodejs/node_modules/core-util-is/package.json b/nodejs/node_modules/core-util-is/package.json
index 688e561f2..7cb0a6ecb 100644
--- a/nodejs/node_modules/core-util-is/package.json
+++ b/nodejs/node_modules/core-util-is/package.json
@@ -1,46 +1,32 @@
{
"_args": [
[
- {
- "raw": "core-util-is@~1.0.0",
- "scope": null,
- "escapedName": "core-util-is",
- "name": "core-util-is",
- "rawSpec": "~1.0.0",
- "spec": ">=1.0.0 <1.1.0",
- "type": "range"
- },
- "/Users/muammar/github/mkchromecast/nodejs/node_modules/readable-stream"
+ "core-util-is@1.0.2",
+ "/Users/muammar/github/mkchromecast/nodejs"
]
],
- "_from": "core-util-is@>=1.0.0 <1.1.0",
+ "_from": "core-util-is@1.0.2",
"_id": "core-util-is@1.0.2",
- "_inCache": true,
+ "_inBundle": false,
+ "_integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=",
"_location": "/core-util-is",
- "_nodeVersion": "4.0.0",
- "_npmUser": {
- "name": "isaacs",
- "email": "i@izs.me"
- },
- "_npmVersion": "3.3.2",
"_phantomChildren": {},
"_requested": {
- "raw": "core-util-is@~1.0.0",
- "scope": null,
- "escapedName": "core-util-is",
+ "type": "version",
+ "registry": true,
+ "raw": "core-util-is@1.0.2",
"name": "core-util-is",
- "rawSpec": "~1.0.0",
- "spec": ">=1.0.0 <1.1.0",
- "type": "range"
+ "escapedName": "core-util-is",
+ "rawSpec": "1.0.2",
+ "saveSpec": null,
+ "fetchSpec": "1.0.2"
},
"_requiredBy": [
"/readable-stream"
],
"_resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
- "_shasum": "b5fd54220aa2bc5ab57aab7140c940754503c1a7",
- "_shrinkwrap": null,
- "_spec": "core-util-is@~1.0.0",
- "_where": "/Users/muammar/github/mkchromecast/nodejs/node_modules/readable-stream",
+ "_spec": "1.0.2",
+ "_where": "/Users/muammar/github/mkchromecast/nodejs",
"author": {
"name": "Isaac Z. Schlueter",
"email": "i@izs.me",
@@ -49,17 +35,10 @@
"bugs": {
"url": "https://github.com/isaacs/core-util-is/issues"
},
- "dependencies": {},
"description": "The `util.is*` functions introduced in Node v0.12.",
"devDependencies": {
"tap": "^2.3.0"
},
- "directories": {},
- "dist": {
- "shasum": "b5fd54220aa2bc5ab57aab7140c940754503c1a7",
- "tarball": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz"
- },
- "gitHead": "a177da234df5638b363ddc15fa324619a38577c8",
"homepage": "https://github.com/isaacs/core-util-is#readme",
"keywords": [
"util",
@@ -74,15 +53,7 @@
],
"license": "MIT",
"main": "lib/util.js",
- "maintainers": [
- {
- "name": "isaacs",
- "email": "i@izs.me"
- }
- ],
"name": "core-util-is",
- "optionalDependencies": {},
- "readme": "ERROR: No README data found!",
"repository": {
"type": "git",
"url": "git://github.com/isaacs/core-util-is.git"
diff --git a/nodejs/node_modules/debug/CHANGELOG.md b/nodejs/node_modules/debug/CHANGELOG.md
index 93580f1cc..eadaa1895 100644
--- a/nodejs/node_modules/debug/CHANGELOG.md
+++ b/nodejs/node_modules/debug/CHANGELOG.md
@@ -1,8 +1,20 @@
-2.6.6 / 2017-04-27
+2.6.9 / 2017-09-22
==================
- * Fix: regression from removal of undefined check (@thebigredgeek)
+ * remove ReDoS regexp in %o formatter (#504)
+
+2.6.8 / 2017-05-18
+==================
+
+ * Fix: Check for undefined on browser globals (#462, @marbemac)
+
+2.6.7 / 2017-05-16
+==================
+
+ * Fix: Update ms to 2.0.0 to fix regular expression denial of service vulnerability (#458, @hubdotcom)
+ * Fix: Inline extend function in node implementation (#452, @dougwilson)
+ * Docs: Fix typo (#455, @msasad)
2.6.5 / 2017-04-27
==================
diff --git a/nodejs/node_modules/debug/Readme.md b/nodejs/node_modules/debug/Readme.md
index 4aeab13f1..f67be6b31 100644
--- a/nodejs/node_modules/debug/Readme.md
+++ b/nodejs/node_modules/debug/Readme.md
@@ -100,7 +100,7 @@ Then, run the program to be debugged as usual.
| Name | Purpose |
|-----------|-------------------------------------------------|
-| `DEBUG` | Enables/disabled specific debugging namespaces. |
+| `DEBUG` | Enables/disables specific debugging namespaces. |
| `DEBUG_COLORS`| Whether or not to use colors in the debug output. |
| `DEBUG_DEPTH` | Object inspection depth. |
| `DEBUG_SHOW_HIDDEN` | Shows hidden properties on inspected objects. |
diff --git a/nodejs/node_modules/debug/component.json b/nodejs/node_modules/debug/component.json
index 0705b91aa..9de26410f 100644
--- a/nodejs/node_modules/debug/component.json
+++ b/nodejs/node_modules/debug/component.json
@@ -2,7 +2,7 @@
"name": "debug",
"repo": "visionmedia/debug",
"description": "small debugging utility",
- "version": "2.6.6",
+ "version": "2.6.9",
"keywords": [
"debug",
"log",
diff --git a/nodejs/node_modules/debug/package.json b/nodejs/node_modules/debug/package.json
index 84ffebf82..09b809a70 100644
--- a/nodejs/node_modules/debug/package.json
+++ b/nodejs/node_modules/debug/package.json
@@ -1,52 +1,38 @@
{
"_args": [
[
- {
- "raw": "debug@^2.0.0",
- "scope": null,
- "escapedName": "debug",
- "name": "debug",
- "rawSpec": "^2.0.0",
- "spec": ">=2.0.0 <3.0.0",
- "type": "range"
- },
- "/Users/muammar/github/mkchromecast/nodejs/node_modules/webcast-osx-audio"
+ "debug@2.6.9",
+ "/Users/muammar/github/mkchromecast/nodejs"
]
],
- "_from": "debug@>=2.0.0 <3.0.0",
- "_id": "debug@2.6.6",
- "_inCache": true,
+ "_from": "debug@2.6.9",
+ "_id": "debug@2.6.9",
+ "_inBundle": false,
+ "_integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"_location": "/debug",
- "_nodeVersion": "6.9.2",
- "_npmOperationalInternal": {
- "host": "packages-12-west.internal.npmjs.com",
- "tmp": "tmp/debug-2.6.6.tgz_1493336101823_0.35170009173452854"
- },
- "_npmUser": {
- "name": "thebigredgeek",
- "email": "rhyneandrew@gmail.com"
- },
- "_npmVersion": "3.10.9",
"_phantomChildren": {},
"_requested": {
- "raw": "debug@^2.0.0",
- "scope": null,
- "escapedName": "debug",
+ "type": "version",
+ "registry": true,
+ "raw": "debug@2.6.9",
"name": "debug",
- "rawSpec": "^2.0.0",
- "spec": ">=2.0.0 <3.0.0",
- "type": "range"
+ "escapedName": "debug",
+ "rawSpec": "2.6.9",
+ "saveSpec": null,
+ "fetchSpec": "2.6.9"
},
"_requiredBy": [
+ "/body-parser",
+ "/express",
+ "/finalhandler",
"/lame",
"/osx-audio",
+ "/send",
"/webcast-osx-audio"
],
- "_resolved": "https://registry.npmjs.org/debug/-/debug-2.6.6.tgz",
- "_shasum": "a9fa6fbe9ca43cf1e79f73b75c0189cbb7d6db5a",
- "_shrinkwrap": null,
- "_spec": "debug@^2.0.0",
- "_where": "/Users/muammar/github/mkchromecast/nodejs/node_modules/webcast-osx-audio",
+ "_resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "_spec": "2.6.9",
+ "_where": "/Users/muammar/github/mkchromecast/nodejs",
"author": {
"name": "TJ Holowaychuk",
"email": "tj@vision-media.ca"
@@ -73,7 +59,7 @@
}
],
"dependencies": {
- "ms": "0.7.3"
+ "ms": "2.0.0"
},
"description": "small debugging utility",
"devDependencies": {
@@ -94,12 +80,6 @@
"sinon": "^1.17.6",
"sinon-chai": "^2.8.0"
},
- "directories": {},
- "dist": {
- "shasum": "a9fa6fbe9ca43cf1e79f73b75c0189cbb7d6db5a",
- "tarball": "https://registry.npmjs.org/debug/-/debug-2.6.6.tgz"
- },
- "gitHead": "c90a2b3c6c17300f3c183f0d665092c16388c7ff",
"homepage": "https://github.com/visionmedia/debug#readme",
"keywords": [
"debug",
@@ -108,19 +88,10 @@
],
"license": "MIT",
"main": "./src/index.js",
- "maintainers": [
- {
- "name": "thebigredgeek",
- "email": "rhyneandrew@gmail.com"
- }
- ],
"name": "debug",
- "optionalDependencies": {},
- "readme": "ERROR: No README data found!",
"repository": {
"type": "git",
"url": "git://github.com/visionmedia/debug.git"
},
- "scripts": {},
- "version": "2.6.6"
+ "version": "2.6.9"
}
diff --git a/nodejs/node_modules/debug/src/browser.js b/nodejs/node_modules/debug/src/browser.js
index 053f4b898..710692493 100644
--- a/nodejs/node_modules/debug/src/browser.js
+++ b/nodejs/node_modules/debug/src/browser.js
@@ -46,14 +46,14 @@ function useColors() {
// is webkit? http://stackoverflow.com/a/16459606/376773
// document is undefined in react-native: https://github.com/facebook/react-native/pull/1632
- return (typeof document !== 'undefined' && document && document.documentElement && document.documentElement.style && document.documentElement.style.WebkitAppearance) ||
+ return (typeof document !== 'undefined' && document.documentElement && document.documentElement.style && document.documentElement.style.WebkitAppearance) ||
// is firebug? http://stackoverflow.com/a/398120/376773
- (typeof window !== 'undefined' && window && window.console && (window.console.firebug || (window.console.exception && window.console.table))) ||
+ (typeof window !== 'undefined' && window.console && (window.console.firebug || (window.console.exception && window.console.table))) ||
// is firefox >= v31?
// https://developer.mozilla.org/en-US/docs/Tools/Web_Console#Styling_messages
- (typeof navigator !== 'undefined' && navigator && navigator.userAgent && navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/) && parseInt(RegExp.$1, 10) >= 31) ||
+ (typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/) && parseInt(RegExp.$1, 10) >= 31) ||
// double check webkit in userAgent just in case we are in a worker
- (typeof navigator !== 'undefined' && navigator && navigator.userAgent && navigator.userAgent.toLowerCase().match(/applewebkit\/(\d+)/));
+ (typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/applewebkit\/(\d+)/));
}
/**
diff --git a/nodejs/node_modules/debug/src/inspector-log.js b/nodejs/node_modules/debug/src/inspector-log.js
new file mode 100644
index 000000000..60ea6c04a
--- /dev/null
+++ b/nodejs/node_modules/debug/src/inspector-log.js
@@ -0,0 +1,15 @@
+module.exports = inspectorLog;
+
+// black hole
+const nullStream = new (require('stream').Writable)();
+nullStream._write = () => {};
+
+/**
+ * Outputs a `console.log()` to the Node.js Inspector console *only*.
+ */
+function inspectorLog() {
+ const stdout = console._stdout;
+ console._stdout = nullStream;
+ console.log.apply(console, arguments);
+ console._stdout = stdout;
+}
diff --git a/nodejs/node_modules/debug/src/node.js b/nodejs/node_modules/debug/src/node.js
index 3c7407b6b..b15109c90 100644
--- a/nodejs/node_modules/debug/src/node.js
+++ b/nodejs/node_modules/debug/src/node.js
@@ -85,7 +85,9 @@ function useColors() {
exports.formatters.o = function(v) {
this.inspectOpts.colors = this.useColors;
return util.inspect(v, this.inspectOpts)
- .replace(/\s*\n\s*/g, ' ');
+ .split('\n').map(function(str) {
+ return str.trim()
+ }).join(' ');
};
/**
@@ -231,7 +233,12 @@ function createWritableStdioStream (fd) {
*/
function init (debug) {
- debug.inspectOpts = util._extend({}, exports.inspectOpts);
+ debug.inspectOpts = {};
+
+ var keys = Object.keys(exports.inspectOpts);
+ for (var i = 0; i < keys.length; i++) {
+ debug.inspectOpts[keys[i]] = exports.inspectOpts[keys[i]];
+ }
}
/**
diff --git a/nodejs/node_modules/depd/History.md b/nodejs/node_modules/depd/History.md
index ace117154..f0016499d 100644
--- a/nodejs/node_modules/depd/History.md
+++ b/nodejs/node_modules/depd/History.md
@@ -1,3 +1,9 @@
+1.1.1 / 2017-07-27
+==================
+
+ * Remove unnecessary `Buffer` loading
+ * Support Node.js 0.6 to 8.x
+
1.1.0 / 2015-09-14
==================
diff --git a/nodejs/node_modules/depd/LICENSE b/nodejs/node_modules/depd/LICENSE
index 142ede386..84441fbb5 100644
--- a/nodejs/node_modules/depd/LICENSE
+++ b/nodejs/node_modules/depd/LICENSE
@@ -1,6 +1,6 @@
(The MIT License)
-Copyright (c) 2014-2015 Douglas Christopher Wilson
+Copyright (c) 2014-2017 Douglas Christopher Wilson
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
diff --git a/nodejs/node_modules/depd/Readme.md b/nodejs/node_modules/depd/Readme.md
index 09bb97995..9e7d8726c 100644
--- a/nodejs/node_modules/depd/Readme.md
+++ b/nodejs/node_modules/depd/Readme.md
@@ -27,6 +27,8 @@ track deprecations.
## API
+
+
```js
var deprecate = require('depd')('my-module')
```
@@ -200,7 +202,7 @@ var deprecate = require('depd')('my-cool-module')
// message automatically derived from function name
// Object.oldfunction
-exports.oldfunction = deprecate.function(function oldfunction() {
+exports.oldfunction = deprecate.function(function oldfunction () {
// all calls to function are deprecated
})
@@ -276,6 +278,6 @@ deprecate.property(exports, 'oldprop', 'oldprop >= 0.10')
[coveralls-image]: https://img.shields.io/coveralls/dougwilson/nodejs-depd/master.svg
[coveralls-url]: https://coveralls.io/r/dougwilson/nodejs-depd?branch=master
[node-image]: https://img.shields.io/node/v/depd.svg
-[node-url]: http://nodejs.org/download/
+[node-url]: https://nodejs.org/en/download/
[gratipay-image]: https://img.shields.io/gratipay/dougwilson.svg
[gratipay-url]: https://www.gratipay.com/dougwilson/
diff --git a/nodejs/node_modules/depd/index.js b/nodejs/node_modules/depd/index.js
index fddcae875..73d81ab54 100644
--- a/nodejs/node_modules/depd/index.js
+++ b/nodejs/node_modules/depd/index.js
@@ -1,6 +1,6 @@
/*!
* depd
- * Copyright(c) 2014-2015 Douglas Christopher Wilson
+ * Copyright(c) 2014-2017 Douglas Christopher Wilson
* MIT Licensed
*/
@@ -28,13 +28,13 @@ var basePath = process.cwd()
* Determine if namespace is contained in the string.
*/
-function containsNamespace(str, namespace) {
+function containsNamespace (str, namespace) {
var val = str.split(/[ ,]+/)
namespace = String(namespace).toLowerCase()
- for (var i = 0 ; i < val.length; i++) {
- if (!(str = val[i])) continue;
+ for (var i = 0; i < val.length; i++) {
+ if (!(str = val[i])) continue
// namespace contained
if (str === '*' || str.toLowerCase() === namespace) {
@@ -49,14 +49,14 @@ function containsNamespace(str, namespace) {
* Convert a data descriptor to accessor descriptor.
*/
-function convertDataDescriptorToAccessor(obj, prop, message) {
+function convertDataDescriptorToAccessor (obj, prop, message) {
var descriptor = Object.getOwnPropertyDescriptor(obj, prop)
var value = descriptor.value
- descriptor.get = function getter() { return value }
+ descriptor.get = function getter () { return value }
if (descriptor.writable) {
- descriptor.set = function setter(val) { return value = val }
+ descriptor.set = function setter (val) { return (value = val) }
}
delete descriptor.value
@@ -71,7 +71,7 @@ function convertDataDescriptorToAccessor(obj, prop, message) {
* Create arguments string to keep arity.
*/
-function createArgumentsString(arity) {
+function createArgumentsString (arity) {
var str = ''
for (var i = 0; i < arity; i++) {
@@ -85,7 +85,7 @@ function createArgumentsString(arity) {
* Create stack string from stack.
*/
-function createStackString(stack) {
+function createStackString (stack) {
var str = this.name + ': ' + this.namespace
if (this.message) {
@@ -103,7 +103,7 @@ function createStackString(stack) {
* Create deprecate for namespace in caller.
*/
-function depd(namespace) {
+function depd (namespace) {
if (!namespace) {
throw new TypeError('argument namespace is required')
}
@@ -112,7 +112,7 @@ function depd(namespace) {
var site = callSiteLocation(stack[1])
var file = site[0]
- function deprecate(message) {
+ function deprecate (message) {
// call to self as log
log.call(deprecate, message)
}
@@ -133,7 +133,7 @@ function depd(namespace) {
* Determine if namespace is ignored.
*/
-function isignored(namespace) {
+function isignored (namespace) {
/* istanbul ignore next: tested in a child processs */
if (process.noDeprecation) {
// --no-deprecation support
@@ -150,7 +150,7 @@ function isignored(namespace) {
* Determine if namespace is traced.
*/
-function istraced(namespace) {
+function istraced (namespace) {
/* istanbul ignore next: tested in a child processs */
if (process.traceDeprecation) {
// --trace-deprecation support
@@ -167,7 +167,7 @@ function istraced(namespace) {
* Display deprecation message.
*/
-function log(message, site) {
+function log (message, site) {
var haslisteners = eventListenerCount(process, 'deprecation') !== 0
// abort early if no destination
@@ -240,15 +240,13 @@ function log(message, site) {
: formatPlain
var msg = format.call(this, message, caller, stack.slice(i))
process.stderr.write(msg + '\n', 'utf8')
-
- return
}
/**
* Get call site location as array.
*/
-function callSiteLocation(callSite) {
+function callSiteLocation (callSite) {
var file = callSite.getFileName() || ''
var line = callSite.getLineNumber()
var colm = callSite.getColumnNumber()
@@ -269,7 +267,7 @@ function callSiteLocation(callSite) {
* Generate a default message from the site.
*/
-function defaultMessage(site) {
+function defaultMessage (site) {
var callSite = site.callSite
var funcName = site.name
@@ -300,12 +298,12 @@ function defaultMessage(site) {
* Format deprecation message without color.
*/
-function formatPlain(msg, caller, stack) {
+function formatPlain (msg, caller, stack) {
var timestamp = new Date().toUTCString()
- var formatted = timestamp
- + ' ' + this._namespace
- + ' deprecated ' + msg
+ var formatted = timestamp +
+ ' ' + this._namespace +
+ ' deprecated ' + msg
// add stack trace
if (this._traced) {
@@ -327,10 +325,10 @@ function formatPlain(msg, caller, stack) {
* Format deprecation message with color.
*/
-function formatColor(msg, caller, stack) {
- var formatted = '\x1b[36;1m' + this._namespace + '\x1b[22;39m' // bold cyan
- + ' \x1b[33;1mdeprecated\x1b[22;39m' // bold yellow
- + ' \x1b[0m' + msg + '\x1b[39m' // reset
+function formatColor (msg, caller, stack) {
+ var formatted = '\x1b[36;1m' + this._namespace + '\x1b[22;39m' + // bold cyan
+ ' \x1b[33;1mdeprecated\x1b[22;39m' + // bold yellow
+ ' \x1b[0m' + msg + '\x1b[39m' // reset
// add stack trace
if (this._traced) {
@@ -352,17 +350,17 @@ function formatColor(msg, caller, stack) {
* Format call site location.
*/
-function formatLocation(callSite) {
- return relative(basePath, callSite[0])
- + ':' + callSite[1]
- + ':' + callSite[2]
+function formatLocation (callSite) {
+ return relative(basePath, callSite[0]) +
+ ':' + callSite[1] +
+ ':' + callSite[2]
}
/**
* Get the stack as array of call sites.
*/
-function getStack() {
+function getStack () {
var limit = Error.stackTraceLimit
var obj = {}
var prep = Error.prepareStackTrace
@@ -386,7 +384,7 @@ function getStack() {
* Capture call site stack from v8.
*/
-function prepareObjectStackTrace(obj, stack) {
+function prepareObjectStackTrace (obj, stack) {
return stack
}
@@ -394,23 +392,24 @@ function prepareObjectStackTrace(obj, stack) {
* Return a wrapped function in a deprecation message.
*/
-function wrapfunction(fn, message) {
+function wrapfunction (fn, message) {
if (typeof fn !== 'function') {
throw new TypeError('argument fn must be a function')
}
var args = createArgumentsString(fn.length)
- var deprecate = this
+ var deprecate = this // eslint-disable-line no-unused-vars
var stack = getStack()
var site = callSiteLocation(stack[1])
site.name = fn.name
- var deprecatedfn = eval('(function (' + args + ') {\n'
- + '"use strict"\n'
- + 'log.call(deprecate, message, site)\n'
- + 'return fn.apply(this, arguments)\n'
- + '})')
+ // eslint-disable-next-line no-eval
+ var deprecatedfn = eval('(function (' + args + ') {\n' +
+ '"use strict"\n' +
+ 'log.call(deprecate, message, site)\n' +
+ 'return fn.apply(this, arguments)\n' +
+ '})')
return deprecatedfn
}
@@ -419,7 +418,7 @@ function wrapfunction(fn, message) {
* Wrap property in a deprecation message.
*/
-function wrapproperty(obj, prop, message) {
+function wrapproperty (obj, prop, message) {
if (!obj || (typeof obj !== 'object' && typeof obj !== 'function')) {
throw new TypeError('argument obj must be object')
}
@@ -451,7 +450,7 @@ function wrapproperty(obj, prop, message) {
// wrap getter
if (typeof get === 'function') {
- descriptor.get = function getter() {
+ descriptor.get = function getter () {
log.call(deprecate, message, site)
return get.apply(this, arguments)
}
@@ -459,7 +458,7 @@ function wrapproperty(obj, prop, message) {
// wrap setter
if (typeof set === 'function') {
- descriptor.set = function setter() {
+ descriptor.set = function setter () {
log.call(deprecate, message, site)
return set.apply(this, arguments)
}
@@ -472,7 +471,7 @@ function wrapproperty(obj, prop, message) {
* Create DeprecationError for deprecation
*/
-function DeprecationError(namespace, message, stack) {
+function DeprecationError (namespace, message, stack) {
var error = new Error()
var stackString
@@ -510,9 +509,9 @@ function DeprecationError(namespace, message, stack) {
}
// prepare stack trace
- return stackString = createStackString.call(this, stack)
+ return (stackString = createStackString.call(this, stack))
},
- set: function setter(val) {
+ set: function setter (val) {
stackString = val
}
})
diff --git a/nodejs/node_modules/depd/lib/browser/index.js b/nodejs/node_modules/depd/lib/browser/index.js
index f464e0525..6be45cc20 100644
--- a/nodejs/node_modules/depd/lib/browser/index.js
+++ b/nodejs/node_modules/depd/lib/browser/index.js
@@ -17,12 +17,12 @@ module.exports = depd
* Create deprecate for namespace in caller.
*/
-function depd(namespace) {
+function depd (namespace) {
if (!namespace) {
throw new TypeError('argument namespace is required')
}
- function deprecate(message) {
+ function deprecate (message) {
// no-op in browser
}
@@ -45,7 +45,7 @@ function depd(namespace) {
* validation.
*/
-function wrapfunction(fn, message) {
+function wrapfunction (fn, message) {
if (typeof fn !== 'function') {
throw new TypeError('argument fn must be a function')
}
@@ -60,7 +60,7 @@ function wrapfunction(fn, message) {
* validation.
*/
-function wrapproperty(obj, prop, message) {
+function wrapproperty (obj, prop, message) {
if (!obj || (typeof obj !== 'object' && typeof obj !== 'function')) {
throw new TypeError('argument obj must be object')
}
@@ -74,6 +74,4 @@ function wrapproperty(obj, prop, message) {
if (!descriptor.configurable) {
throw new TypeError('property must be configurable')
}
-
- return
}
diff --git a/nodejs/node_modules/depd/lib/compat/buffer-concat.js b/nodejs/node_modules/depd/lib/compat/buffer-concat.js
deleted file mode 100644
index 4b7338102..000000000
--- a/nodejs/node_modules/depd/lib/compat/buffer-concat.js
+++ /dev/null
@@ -1,35 +0,0 @@
-/*!
- * depd
- * Copyright(c) 2014 Douglas Christopher Wilson
- * MIT Licensed
- */
-
-'use strict'
-
-/**
- * Module exports.
- */
-
-module.exports = bufferConcat
-
-/**
- * Concatenate an array of Buffers.
- */
-
-function bufferConcat(bufs) {
- var length = 0
-
- for (var i = 0, len = bufs.length; i < len; i++) {
- length += bufs[i].length
- }
-
- var buf = new Buffer(length)
- var pos = 0
-
- for (var i = 0, len = bufs.length; i < len; i++) {
- bufs[i].copy(buf, pos)
- pos += bufs[i].length
- }
-
- return buf
-}
diff --git a/nodejs/node_modules/depd/lib/compat/callsite-tostring.js b/nodejs/node_modules/depd/lib/compat/callsite-tostring.js
index 9ecef3466..73186dc64 100644
--- a/nodejs/node_modules/depd/lib/compat/callsite-tostring.js
+++ b/nodejs/node_modules/depd/lib/compat/callsite-tostring.js
@@ -16,7 +16,7 @@ module.exports = callSiteToString
* Format a CallSite file location to a string.
*/
-function callSiteFileLocation(callSite) {
+function callSiteFileLocation (callSite) {
var fileName
var fileLocation = ''
@@ -52,7 +52,7 @@ function callSiteFileLocation(callSite) {
* Format a CallSite to a string.
*/
-function callSiteToString(callSite) {
+function callSiteToString (callSite) {
var addSuffix = true
var fileLocation = callSiteFileLocation(callSite)
var functionName = callSite.getFunctionName()
@@ -97,7 +97,7 @@ function callSiteToString(callSite) {
* Get constructor name of reviver.
*/
-function getConstructorName(obj) {
+function getConstructorName (obj) {
var receiver = obj.receiver
return (receiver.constructor && receiver.constructor.name) || null
}
diff --git a/nodejs/node_modules/depd/lib/compat/event-listener-count.js b/nodejs/node_modules/depd/lib/compat/event-listener-count.js
index a05fceb79..3a8925d13 100644
--- a/nodejs/node_modules/depd/lib/compat/event-listener-count.js
+++ b/nodejs/node_modules/depd/lib/compat/event-listener-count.js
@@ -17,6 +17,6 @@ module.exports = eventListenerCount
* Get the count of listeners on an event emitter of a specific type.
*/
-function eventListenerCount(emitter, type) {
+function eventListenerCount (emitter, type) {
return emitter.listeners(type).length
}
diff --git a/nodejs/node_modules/depd/lib/compat/index.js b/nodejs/node_modules/depd/lib/compat/index.js
index aa3c1de43..955b3336b 100644
--- a/nodejs/node_modules/depd/lib/compat/index.js
+++ b/nodejs/node_modules/depd/lib/compat/index.js
@@ -11,7 +11,6 @@
* @private
*/
-var Buffer = require('buffer')
var EventEmitter = require('events').EventEmitter
/**
@@ -19,16 +18,12 @@ var EventEmitter = require('events').EventEmitter
* @public
*/
-lazyProperty(module.exports, 'bufferConcat', function bufferConcat() {
- return Buffer.concat || require('./buffer-concat')
-})
-
-lazyProperty(module.exports, 'callSiteToString', function callSiteToString() {
+lazyProperty(module.exports, 'callSiteToString', function callSiteToString () {
var limit = Error.stackTraceLimit
var obj = {}
var prep = Error.prepareStackTrace
- function prepareObjectStackTrace(obj, stack) {
+ function prepareObjectStackTrace (obj, stack) {
return stack
}
@@ -47,7 +42,7 @@ lazyProperty(module.exports, 'callSiteToString', function callSiteToString() {
return stack[0].toString ? toString : require('./callsite-tostring')
})
-lazyProperty(module.exports, 'eventListenerCount', function eventListenerCount() {
+lazyProperty(module.exports, 'eventListenerCount', function eventListenerCount () {
return EventEmitter.listenerCount || require('./event-listener-count')
})
@@ -55,8 +50,8 @@ lazyProperty(module.exports, 'eventListenerCount', function eventListenerCount()
* Define a lazy property.
*/
-function lazyProperty(obj, prop, getter) {
- function get() {
+function lazyProperty (obj, prop, getter) {
+ function get () {
var val = getter()
Object.defineProperty(obj, prop, {
@@ -79,6 +74,6 @@ function lazyProperty(obj, prop, getter) {
* Call toString() on the obj
*/
-function toString(obj) {
+function toString (obj) {
return obj.toString()
}
diff --git a/nodejs/node_modules/depd/package.json b/nodejs/node_modules/depd/package.json
index b8458dea4..d0b5e4109 100644
--- a/nodejs/node_modules/depd/package.json
+++ b/nodejs/node_modules/depd/package.json
@@ -1,47 +1,35 @@
{
"_args": [
[
- {
- "raw": "depd@~1.1.0",
- "scope": null,
- "escapedName": "depd",
- "name": "depd",
- "rawSpec": "~1.1.0",
- "spec": ">=1.1.0 <1.2.0",
- "type": "range"
- },
- "/Users/muammar/github/mkchromecast/nodejs/node_modules/express"
+ "depd@1.1.1",
+ "/Users/muammar/github/mkchromecast/nodejs"
]
],
- "_from": "depd@>=1.1.0 <1.2.0",
- "_id": "depd@1.1.0",
- "_inCache": true,
+ "_from": "depd@1.1.1",
+ "_id": "depd@1.1.1",
+ "_inBundle": false,
+ "_integrity": "sha1-V4O04cRZ8G+lyif5kfPQbnoxA1k=",
"_location": "/depd",
- "_npmUser": {
- "name": "dougwilson",
- "email": "doug@somethingdoug.com"
- },
- "_npmVersion": "1.4.28",
"_phantomChildren": {},
"_requested": {
- "raw": "depd@~1.1.0",
- "scope": null,
- "escapedName": "depd",
+ "type": "version",
+ "registry": true,
+ "raw": "depd@1.1.1",
"name": "depd",
- "rawSpec": "~1.1.0",
- "spec": ">=1.1.0 <1.2.0",
- "type": "range"
+ "escapedName": "depd",
+ "rawSpec": "1.1.1",
+ "saveSpec": null,
+ "fetchSpec": "1.1.1"
},
"_requiredBy": [
+ "/body-parser",
"/express",
"/http-errors",
"/send"
],
- "_resolved": "https://registry.npmjs.org/depd/-/depd-1.1.0.tgz",
- "_shasum": "e1bd82c6aab6ced965b97b88b17ed3e528ca18c3",
- "_shrinkwrap": null,
- "_spec": "depd@~1.1.0",
- "_where": "/Users/muammar/github/mkchromecast/nodejs/node_modules/express",
+ "_resolved": "https://registry.npmjs.org/depd/-/depd-1.1.1.tgz",
+ "_spec": "1.1.1",
+ "_where": "/Users/muammar/github/mkchromecast/nodejs",
"author": {
"name": "Douglas Christopher Wilson",
"email": "doug@somethingdoug.com"
@@ -50,19 +38,18 @@
"bugs": {
"url": "https://github.com/dougwilson/nodejs-depd/issues"
},
- "dependencies": {},
"description": "Deprecate all the things",
"devDependencies": {
"beautify-benchmark": "0.2.4",
- "benchmark": "1.0.0",
- "istanbul": "0.3.5",
+ "benchmark": "2.1.4",
+ "eslint": "3.19.0",
+ "eslint-config-standard": "7.1.0",
+ "eslint-plugin-markdown": "1.0.0-beta.7",
+ "eslint-plugin-promise": "3.5.0",
+ "eslint-plugin-standard": "2.3.1",
+ "istanbul": "0.4.5",
"mocha": "~1.21.5"
},
- "directories": {},
- "dist": {
- "shasum": "e1bd82c6aab6ced965b97b88b17ed3e528ca18c3",
- "tarball": "https://registry.npmjs.org/depd/-/depd-1.1.0.tgz"
- },
"engines": {
"node": ">= 0.6"
},
@@ -73,31 +60,23 @@
"index.js",
"Readme.md"
],
- "gitHead": "78c659de20283e3a6bee92bda455e6daff01686a",
- "homepage": "https://github.com/dougwilson/nodejs-depd",
+ "homepage": "https://github.com/dougwilson/nodejs-depd#readme",
"keywords": [
"deprecate",
"deprecated"
],
"license": "MIT",
- "maintainers": [
- {
- "name": "dougwilson",
- "email": "doug@somethingdoug.com"
- }
- ],
"name": "depd",
- "optionalDependencies": {},
- "readme": "ERROR: No README data found!",
"repository": {
"type": "git",
"url": "git+https://github.com/dougwilson/nodejs-depd.git"
},
"scripts": {
"bench": "node benchmark/index.js",
+ "lint": "eslint --plugin markdown --ext js,md .",
"test": "mocha --reporter spec --bail test/",
"test-ci": "istanbul cover node_modules/mocha/bin/_mocha --report lcovonly -- --reporter spec --no-exit test/",
"test-cov": "istanbul cover node_modules/mocha/bin/_mocha -- --reporter dot test/"
},
- "version": "1.1.0"
+ "version": "1.1.1"
}
diff --git a/nodejs/node_modules/destroy/package.json b/nodejs/node_modules/destroy/package.json
index d7e451b2c..75fb52660 100644
--- a/nodejs/node_modules/destroy/package.json
+++ b/nodejs/node_modules/destroy/package.json
@@ -1,45 +1,32 @@
{
"_args": [
[
- {
- "raw": "destroy@~1.0.4",
- "scope": null,
- "escapedName": "destroy",
- "name": "destroy",
- "rawSpec": "~1.0.4",
- "spec": ">=1.0.4 <1.1.0",
- "type": "range"
- },
- "/Users/muammar/github/mkchromecast/nodejs/node_modules/send"
+ "destroy@1.0.4",
+ "/Users/muammar/github/mkchromecast/nodejs"
]
],
- "_from": "destroy@>=1.0.4 <1.1.0",
+ "_from": "destroy@1.0.4",
"_id": "destroy@1.0.4",
- "_inCache": true,
+ "_inBundle": false,
+ "_integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=",
"_location": "/destroy",
- "_npmUser": {
- "name": "dougwilson",
- "email": "doug@somethingdoug.com"
- },
- "_npmVersion": "1.4.28",
"_phantomChildren": {},
"_requested": {
- "raw": "destroy@~1.0.4",
- "scope": null,
- "escapedName": "destroy",
+ "type": "version",
+ "registry": true,
+ "raw": "destroy@1.0.4",
"name": "destroy",
- "rawSpec": "~1.0.4",
- "spec": ">=1.0.4 <1.1.0",
- "type": "range"
+ "escapedName": "destroy",
+ "rawSpec": "1.0.4",
+ "saveSpec": null,
+ "fetchSpec": "1.0.4"
},
"_requiredBy": [
"/send"
],
"_resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
- "_shasum": "978857442c44749e4206613e37946205826abd80",
- "_shrinkwrap": null,
- "_spec": "destroy@~1.0.4",
- "_where": "/Users/muammar/github/mkchromecast/nodejs/node_modules/send",
+ "_spec": "1.0.4",
+ "_where": "/Users/muammar/github/mkchromecast/nodejs",
"author": {
"name": "Jonathan Ong",
"email": "me@jongleberry.com",
@@ -54,23 +41,16 @@
"email": "doug@somethingdoug.com"
}
],
- "dependencies": {},
"description": "destroy a stream if possible",
"devDependencies": {
"istanbul": "0.4.2",
"mocha": "2.3.4"
},
- "directories": {},
- "dist": {
- "shasum": "978857442c44749e4206613e37946205826abd80",
- "tarball": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz"
- },
"files": [
"index.js",
"LICENSE"
],
- "gitHead": "86edea01456f5fa1027f6a47250c34c713cbcc3b",
- "homepage": "https://github.com/stream-utils/destroy",
+ "homepage": "https://github.com/stream-utils/destroy#readme",
"keywords": [
"stream",
"streams",
@@ -80,19 +60,7 @@
"fd"
],
"license": "MIT",
- "maintainers": [
- {
- "name": "jongleberry",
- "email": "jonathanrichardong@gmail.com"
- },
- {
- "name": "dougwilson",
- "email": "doug@somethingdoug.com"
- }
- ],
"name": "destroy",
- "optionalDependencies": {},
- "readme": "ERROR: No README data found!",
"repository": {
"type": "git",
"url": "git+https://github.com/stream-utils/destroy.git"
diff --git a/nodejs/node_modules/ee-first/package.json b/nodejs/node_modules/ee-first/package.json
index 451bca243..8920337ea 100644
--- a/nodejs/node_modules/ee-first/package.json
+++ b/nodejs/node_modules/ee-first/package.json
@@ -1,45 +1,32 @@
{
"_args": [
[
- {
- "raw": "ee-first@1.1.1",
- "scope": null,
- "escapedName": "ee-first",
- "name": "ee-first",
- "rawSpec": "1.1.1",
- "spec": "1.1.1",
- "type": "version"
- },
- "/Users/muammar/github/mkchromecast/nodejs/node_modules/on-finished"
+ "ee-first@1.1.1",
+ "/Users/muammar/github/mkchromecast/nodejs"
]
],
"_from": "ee-first@1.1.1",
"_id": "ee-first@1.1.1",
- "_inCache": true,
+ "_inBundle": false,
+ "_integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=",
"_location": "/ee-first",
- "_npmUser": {
- "name": "dougwilson",
- "email": "doug@somethingdoug.com"
- },
- "_npmVersion": "1.4.28",
"_phantomChildren": {},
"_requested": {
+ "type": "version",
+ "registry": true,
"raw": "ee-first@1.1.1",
- "scope": null,
- "escapedName": "ee-first",
"name": "ee-first",
+ "escapedName": "ee-first",
"rawSpec": "1.1.1",
- "spec": "1.1.1",
- "type": "version"
+ "saveSpec": null,
+ "fetchSpec": "1.1.1"
},
"_requiredBy": [
"/on-finished"
],
"_resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
- "_shasum": "590c61156b0ae2f4f0255732a158b266bc56b21d",
- "_shrinkwrap": null,
- "_spec": "ee-first@1.1.1",
- "_where": "/Users/muammar/github/mkchromecast/nodejs/node_modules/on-finished",
+ "_spec": "1.1.1",
+ "_where": "/Users/muammar/github/mkchromecast/nodejs",
"author": {
"name": "Jonathan Ong",
"email": "me@jongleberry.com",
@@ -54,37 +41,18 @@
"email": "doug@somethingdoug.com"
}
],
- "dependencies": {},
"description": "return the first event in a set of ee/event pairs",
"devDependencies": {
"istanbul": "0.3.9",
"mocha": "2.2.5"
},
- "directories": {},
- "dist": {
- "shasum": "590c61156b0ae2f4f0255732a158b266bc56b21d",
- "tarball": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz"
- },
"files": [
"index.js",
"LICENSE"
],
- "gitHead": "512e0ce4cc3643f603708f965a97b61b1a9c0441",
- "homepage": "https://github.com/jonathanong/ee-first",
+ "homepage": "https://github.com/jonathanong/ee-first#readme",
"license": "MIT",
- "maintainers": [
- {
- "name": "jongleberry",
- "email": "jonathanrichardong@gmail.com"
- },
- {
- "name": "dougwilson",
- "email": "doug@somethingdoug.com"
- }
- ],
"name": "ee-first",
- "optionalDependencies": {},
- "readme": "ERROR: No README data found!",
"repository": {
"type": "git",
"url": "git+https://github.com/jonathanong/ee-first.git"
diff --git a/nodejs/node_modules/encodeurl/package.json b/nodejs/node_modules/encodeurl/package.json
index c55367d2a..5ed89a38b 100644
--- a/nodejs/node_modules/encodeurl/package.json
+++ b/nodejs/node_modules/encodeurl/package.json
@@ -1,41 +1,25 @@
{
"_args": [
[
- {
- "raw": "encodeurl@~1.0.1",
- "scope": null,
- "escapedName": "encodeurl",
- "name": "encodeurl",
- "rawSpec": "~1.0.1",
- "spec": ">=1.0.1 <1.1.0",
- "type": "range"
- },
- "/Users/muammar/github/mkchromecast/nodejs/node_modules/express"
+ "encodeurl@1.0.1",
+ "/Users/muammar/github/mkchromecast/nodejs"
]
],
- "_from": "encodeurl@>=1.0.1 <1.1.0",
+ "_from": "encodeurl@1.0.1",
"_id": "encodeurl@1.0.1",
- "_inCache": true,
+ "_inBundle": false,
+ "_integrity": "sha1-eePVhlU0aQn+bw9Fpd5oEDspTSA=",
"_location": "/encodeurl",
- "_nodeVersion": "4.4.3",
- "_npmOperationalInternal": {
- "host": "packages-12-west.internal.npmjs.com",
- "tmp": "tmp/encodeurl-1.0.1.tgz_1465519736251_0.09314409433864057"
- },
- "_npmUser": {
- "name": "dougwilson",
- "email": "doug@somethingdoug.com"
- },
- "_npmVersion": "2.15.1",
"_phantomChildren": {},
"_requested": {
- "raw": "encodeurl@~1.0.1",
- "scope": null,
- "escapedName": "encodeurl",
+ "type": "version",
+ "registry": true,
+ "raw": "encodeurl@1.0.1",
"name": "encodeurl",
- "rawSpec": "~1.0.1",
- "spec": ">=1.0.1 <1.1.0",
- "type": "range"
+ "escapedName": "encodeurl",
+ "rawSpec": "1.0.1",
+ "saveSpec": null,
+ "fetchSpec": "1.0.1"
},
"_requiredBy": [
"/express",
@@ -44,10 +28,8 @@
"/serve-static"
],
"_resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.1.tgz",
- "_shasum": "79e3d58655346909fe6f0f45a5de68103b294d20",
- "_shrinkwrap": null,
- "_spec": "encodeurl@~1.0.1",
- "_where": "/Users/muammar/github/mkchromecast/nodejs/node_modules/express",
+ "_spec": "1.0.1",
+ "_where": "/Users/muammar/github/mkchromecast/nodejs",
"bugs": {
"url": "https://github.com/pillarjs/encodeurl/issues"
},
@@ -57,7 +39,6 @@
"email": "doug@somethingdoug.com"
}
],
- "dependencies": {},
"description": "Encode a URL to a percent-encoded form, excluding already-encoded sequences",
"devDependencies": {
"eslint": "2.11.1",
@@ -67,11 +48,6 @@
"istanbul": "0.4.3",
"mocha": "2.5.3"
},
- "directories": {},
- "dist": {
- "shasum": "79e3d58655346909fe6f0f45a5de68103b294d20",
- "tarball": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.1.tgz"
- },
"engines": {
"node": ">= 0.8"
},
@@ -81,7 +57,6 @@
"README.md",
"index.js"
],
- "gitHead": "39ed0c235fed4cea7d012038fd6bb0480561d226",
"homepage": "https://github.com/pillarjs/encodeurl#readme",
"keywords": [
"encode",
@@ -89,15 +64,7 @@
"url"
],
"license": "MIT",
- "maintainers": [
- {
- "name": "dougwilson",
- "email": "doug@somethingdoug.com"
- }
- ],
"name": "encodeurl",
- "optionalDependencies": {},
- "readme": "ERROR: No README data found!",
"repository": {
"type": "git",
"url": "git+https://github.com/pillarjs/encodeurl.git"
diff --git a/nodejs/node_modules/escape-html/package.json b/nodejs/node_modules/escape-html/package.json
index bbeab5d78..d18336138 100644
--- a/nodejs/node_modules/escape-html/package.json
+++ b/nodejs/node_modules/escape-html/package.json
@@ -1,36 +1,25 @@
{
"_args": [
[
- {
- "raw": "escape-html@~1.0.3",
- "scope": null,
- "escapedName": "escape-html",
- "name": "escape-html",
- "rawSpec": "~1.0.3",
- "spec": ">=1.0.3 <1.1.0",
- "type": "range"
- },
- "/Users/muammar/github/mkchromecast/nodejs/node_modules/express"
+ "escape-html@1.0.3",
+ "/Users/muammar/github/mkchromecast/nodejs"
]
],
- "_from": "escape-html@>=1.0.3 <1.1.0",
+ "_from": "escape-html@1.0.3",
"_id": "escape-html@1.0.3",
- "_inCache": true,
+ "_inBundle": false,
+ "_integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=",
"_location": "/escape-html",
- "_npmUser": {
- "name": "dougwilson",
- "email": "doug@somethingdoug.com"
- },
- "_npmVersion": "1.4.28",
"_phantomChildren": {},
"_requested": {
- "raw": "escape-html@~1.0.3",
- "scope": null,
- "escapedName": "escape-html",
+ "type": "version",
+ "registry": true,
+ "raw": "escape-html@1.0.3",
"name": "escape-html",
- "rawSpec": "~1.0.3",
- "spec": ">=1.0.3 <1.1.0",
- "type": "range"
+ "escapedName": "escape-html",
+ "rawSpec": "1.0.3",
+ "saveSpec": null,
+ "fetchSpec": "1.0.3"
},
"_requiredBy": [
"/express",
@@ -39,50 +28,29 @@
"/serve-static"
],
"_resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
- "_shasum": "0258eae4d3d0c0974de1c169188ef0051d1d1988",
- "_shrinkwrap": null,
- "_spec": "escape-html@~1.0.3",
- "_where": "/Users/muammar/github/mkchromecast/nodejs/node_modules/express",
+ "_spec": "1.0.3",
+ "_where": "/Users/muammar/github/mkchromecast/nodejs",
"bugs": {
"url": "https://github.com/component/escape-html/issues"
},
- "dependencies": {},
"description": "Escape string for use in HTML",
"devDependencies": {
"beautify-benchmark": "0.2.4",
"benchmark": "1.0.0"
},
- "directories": {},
- "dist": {
- "shasum": "0258eae4d3d0c0974de1c169188ef0051d1d1988",
- "tarball": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz"
- },
"files": [
"LICENSE",
"Readme.md",
"index.js"
],
- "gitHead": "7ac2ea3977fcac3d4c5be8d2a037812820c65f28",
- "homepage": "https://github.com/component/escape-html",
+ "homepage": "https://github.com/component/escape-html#readme",
"keywords": [
"escape",
"html",
"utility"
],
"license": "MIT",
- "maintainers": [
- {
- "name": "tjholowaychuk",
- "email": "tj@vision-media.ca"
- },
- {
- "name": "dougwilson",
- "email": "doug@somethingdoug.com"
- }
- ],
"name": "escape-html",
- "optionalDependencies": {},
- "readme": "ERROR: No README data found!",
"repository": {
"type": "git",
"url": "git+https://github.com/component/escape-html.git"
diff --git a/nodejs/node_modules/escape-string-regexp/package.json b/nodejs/node_modules/escape-string-regexp/package.json
index 8669c3add..b01780a03 100644
--- a/nodejs/node_modules/escape-string-regexp/package.json
+++ b/nodejs/node_modules/escape-string-regexp/package.json
@@ -1,50 +1,32 @@
{
"_args": [
[
- {
- "raw": "escape-string-regexp@^1.0.0",
- "scope": null,
- "escapedName": "escape-string-regexp",
- "name": "escape-string-regexp",
- "rawSpec": "^1.0.0",
- "spec": ">=1.0.0 <2.0.0",
- "type": "range"
- },
- "/Users/muammar/github/mkchromecast/nodejs/node_modules/chalk"
+ "escape-string-regexp@1.0.5",
+ "/Users/muammar/github/mkchromecast/nodejs"
]
],
- "_from": "escape-string-regexp@>=1.0.0 <2.0.0",
+ "_from": "escape-string-regexp@1.0.5",
"_id": "escape-string-regexp@1.0.5",
- "_inCache": true,
+ "_inBundle": false,
+ "_integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
"_location": "/escape-string-regexp",
- "_nodeVersion": "4.2.6",
- "_npmOperationalInternal": {
- "host": "packages-9-west.internal.npmjs.com",
- "tmp": "tmp/escape-string-regexp-1.0.5.tgz_1456059312074_0.7245344955008477"
- },
- "_npmUser": {
- "name": "jbnicolai",
- "email": "jappelman@xebia.com"
- },
- "_npmVersion": "2.14.12",
"_phantomChildren": {},
"_requested": {
- "raw": "escape-string-regexp@^1.0.0",
- "scope": null,
- "escapedName": "escape-string-regexp",
+ "type": "version",
+ "registry": true,
+ "raw": "escape-string-regexp@1.0.5",
"name": "escape-string-regexp",
- "rawSpec": "^1.0.0",
- "spec": ">=1.0.0 <2.0.0",
- "type": "range"
+ "escapedName": "escape-string-regexp",
+ "rawSpec": "1.0.5",
+ "saveSpec": null,
+ "fetchSpec": "1.0.5"
},
"_requiredBy": [
"/chalk"
],
"_resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
- "_shasum": "1b61c0562190a8dff6ae3bb2cf0200ca130b86d4",
- "_shrinkwrap": null,
- "_spec": "escape-string-regexp@^1.0.0",
- "_where": "/Users/muammar/github/mkchromecast/nodejs/node_modules/chalk",
+ "_spec": "1.0.5",
+ "_where": "/Users/muammar/github/mkchromecast/nodejs",
"author": {
"name": "Sindre Sorhus",
"email": "sindresorhus@gmail.com",
@@ -53,25 +35,18 @@
"bugs": {
"url": "https://github.com/sindresorhus/escape-string-regexp/issues"
},
- "dependencies": {},
"description": "Escape RegExp special characters",
"devDependencies": {
"ava": "*",
"xo": "*"
},
- "directories": {},
- "dist": {
- "shasum": "1b61c0562190a8dff6ae3bb2cf0200ca130b86d4",
- "tarball": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz"
- },
"engines": {
"node": ">=0.8.0"
},
"files": [
"index.js"
],
- "gitHead": "db124a3e1aae9d692c4899e42a5c6c3e329eaa20",
- "homepage": "https://github.com/sindresorhus/escape-string-regexp",
+ "homepage": "https://github.com/sindresorhus/escape-string-regexp#readme",
"keywords": [
"escape",
"regex",
@@ -87,17 +62,17 @@
"license": "MIT",
"maintainers": [
{
- "name": "sindresorhus",
- "email": "sindresorhus@gmail.com"
+ "name": "Sindre Sorhus",
+ "email": "sindresorhus@gmail.com",
+ "url": "sindresorhus.com"
},
{
- "name": "jbnicolai",
- "email": "jappelman@xebia.com"
+ "name": "Joshua Boy Nicolai Appelman",
+ "email": "joshua@jbna.nl",
+ "url": "jbna.nl"
}
],
"name": "escape-string-regexp",
- "optionalDependencies": {},
- "readme": "ERROR: No README data found!",
"repository": {
"type": "git",
"url": "git+https://github.com/sindresorhus/escape-string-regexp.git"
diff --git a/nodejs/node_modules/etag/HISTORY.md b/nodejs/node_modules/etag/HISTORY.md
index 136da8c69..222b293de 100644
--- a/nodejs/node_modules/etag/HISTORY.md
+++ b/nodejs/node_modules/etag/HISTORY.md
@@ -1,3 +1,8 @@
+1.8.1 / 2017-09-12
+==================
+
+ * perf: replace regular expression with substring
+
1.8.0 / 2017-02-18
==================
diff --git a/nodejs/node_modules/etag/README.md b/nodejs/node_modules/etag/README.md
index 9963a5fc4..09c2169e7 100644
--- a/nodejs/node_modules/etag/README.md
+++ b/nodejs/node_modules/etag/README.md
@@ -23,7 +23,7 @@ $ npm install etag
## API
-
+
```js
var etag = require('etag')
@@ -36,7 +36,7 @@ body of the entity. Strings, `Buffer`s, and `fs.Stats` are accepted. By
default, a strong ETag is generated except for `fs.Stats`, which will
generate a weak ETag (this can be overwritten by `options.weak`).
-
+
```js
res.setHeader('ETag', etag(body))
@@ -63,18 +63,18 @@ $ npm test
```bash
$ npm run-script bench
-> etag@1.8.0 bench nodejs-etag
+> etag@1.8.1 bench nodejs-etag
> node benchmark/index.js
http_parser@2.7.0
- node@6.9.1
- v8@5.1.281.84
- uv@1.9.1
- zlib@1.2.8
+ node@6.11.1
+ v8@5.1.281.103
+ uv@1.11.0
+ zlib@1.2.11
ares@1.10.1-DEV
- icu@57.1
+ icu@58.2
modules@48
- openssl@1.0.2j
+ openssl@1.0.2k
> node benchmark/body0-100b.js
@@ -82,10 +82,10 @@ $ npm run-script bench
4 tests completed.
-* buffer - strong x 498,600 ops/sec ±0.82% (191 runs sampled)
-* buffer - weak x 496,249 ops/sec ±0.59% (179 runs sampled)
- string - strong x 466,298 ops/sec ±0.88% (186 runs sampled)
- string - weak x 464,298 ops/sec ±0.84% (184 runs sampled)
+ buffer - strong x 258,647 ops/sec ±1.07% (180 runs sampled)
+ buffer - weak x 263,812 ops/sec ±0.61% (184 runs sampled)
+ string - strong x 259,955 ops/sec ±1.19% (185 runs sampled)
+ string - weak x 264,356 ops/sec ±1.09% (184 runs sampled)
> node benchmark/body1-1kb.js
@@ -93,10 +93,10 @@ $ npm run-script bench
4 tests completed.
-* buffer - strong x 346,535 ops/sec ±0.32% (189 runs sampled)
-* buffer - weak x 344,958 ops/sec ±0.52% (185 runs sampled)
- string - strong x 259,672 ops/sec ±0.82% (191 runs sampled)
- string - weak x 260,931 ops/sec ±0.76% (190 runs sampled)
+ buffer - strong x 189,018 ops/sec ±1.12% (182 runs sampled)
+ buffer - weak x 190,586 ops/sec ±0.81% (186 runs sampled)
+ string - strong x 144,272 ops/sec ±0.96% (188 runs sampled)
+ string - weak x 145,380 ops/sec ±1.43% (187 runs sampled)
> node benchmark/body2-5kb.js
@@ -104,10 +104,10 @@ $ npm run-script bench
4 tests completed.
-* buffer - strong x 136,510 ops/sec ±0.62% (189 runs sampled)
-* buffer - weak x 136,604 ops/sec ±0.51% (191 runs sampled)
- string - strong x 80,903 ops/sec ±0.84% (192 runs sampled)
- string - weak x 82,785 ops/sec ±0.50% (193 runs sampled)
+ buffer - strong x 92,435 ops/sec ±0.42% (188 runs sampled)
+ buffer - weak x 92,373 ops/sec ±0.58% (189 runs sampled)
+ string - strong x 48,850 ops/sec ±0.56% (186 runs sampled)
+ string - weak x 49,380 ops/sec ±0.56% (190 runs sampled)
> node benchmark/body3-10kb.js
@@ -115,10 +115,10 @@ $ npm run-script bench
4 tests completed.
-* buffer - strong x 78,650 ops/sec ±0.31% (193 runs sampled)
-* buffer - weak x 78,685 ops/sec ±0.41% (193 runs sampled)
- string - strong x 43,999 ops/sec ±0.43% (193 runs sampled)
- string - weak x 44,081 ops/sec ±0.45% (192 runs sampled)
+ buffer - strong x 55,989 ops/sec ±0.93% (188 runs sampled)
+ buffer - weak x 56,148 ops/sec ±0.55% (190 runs sampled)
+ string - strong x 27,345 ops/sec ±0.43% (188 runs sampled)
+ string - weak x 27,496 ops/sec ±0.45% (190 runs sampled)
> node benchmark/body4-100kb.js
@@ -126,10 +126,10 @@ $ npm run-script bench
4 tests completed.
- buffer - strong x 8,860 ops/sec ±0.66% (191 runs sampled)
-* buffer - weak x 9,030 ops/sec ±0.26% (193 runs sampled)
- string - strong x 4,838 ops/sec ±0.16% (194 runs sampled)
- string - weak x 4,800 ops/sec ±0.52% (192 runs sampled)
+ buffer - strong x 7,083 ops/sec ±0.22% (190 runs sampled)
+ buffer - weak x 7,115 ops/sec ±0.26% (191 runs sampled)
+ string - strong x 3,068 ops/sec ±0.34% (190 runs sampled)
+ string - weak x 3,096 ops/sec ±0.35% (190 runs sampled)
> node benchmark/stats.js
@@ -137,10 +137,10 @@ $ npm run-script bench
4 tests completed.
-* real - strong x 1,468,073 ops/sec ±0.32% (191 runs sampled)
-* real - weak x 1,446,852 ops/sec ±0.64% (190 runs sampled)
- fake - strong x 635,707 ops/sec ±0.33% (194 runs sampled)
- fake - weak x 627,708 ops/sec ±0.36% (192 runs sampled)
+ real - strong x 871,642 ops/sec ±0.34% (189 runs sampled)
+ real - weak x 867,613 ops/sec ±0.39% (190 runs sampled)
+ fake - strong x 401,051 ops/sec ±0.40% (189 runs sampled)
+ fake - weak x 400,100 ops/sec ±0.47% (188 runs sampled)
```
## License
diff --git a/nodejs/node_modules/etag/index.js b/nodejs/node_modules/etag/index.js
index 607f1488e..2a585c91f 100644
--- a/nodejs/node_modules/etag/index.js
+++ b/nodejs/node_modules/etag/index.js
@@ -26,7 +26,6 @@ var Stats = require('fs').Stats
* @private
*/
-var base64PadCharRegExp = /=+$/
var toString = Object.prototype.toString
/**
@@ -48,7 +47,7 @@ function entitytag (entity) {
.createHash('sha1')
.update(entity, 'utf8')
.digest('base64')
- .replace(base64PadCharRegExp, '')
+ .substring(0, 27)
// compute length of entity
var len = typeof entity === 'string'
diff --git a/nodejs/node_modules/etag/package.json b/nodejs/node_modules/etag/package.json
index 4305d5f4c..2732aa2a5 100644
--- a/nodejs/node_modules/etag/package.json
+++ b/nodejs/node_modules/etag/package.json
@@ -1,51 +1,33 @@
{
"_args": [
[
- {
- "raw": "etag@~1.8.0",
- "scope": null,
- "escapedName": "etag",
- "name": "etag",
- "rawSpec": "~1.8.0",
- "spec": ">=1.8.0 <1.9.0",
- "type": "range"
- },
- "/Users/muammar/github/mkchromecast/nodejs/node_modules/express"
+ "etag@1.8.1",
+ "/Users/muammar/github/mkchromecast/nodejs"
]
],
- "_from": "etag@>=1.8.0 <1.9.0",
- "_id": "etag@1.8.0",
- "_inCache": true,
+ "_from": "etag@1.8.1",
+ "_id": "etag@1.8.1",
+ "_inBundle": false,
+ "_integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=",
"_location": "/etag",
- "_nodeVersion": "4.7.3",
- "_npmOperationalInternal": {
- "host": "packages-12-west.internal.npmjs.com",
- "tmp": "tmp/etag-1.8.0.tgz_1487475735517_0.6724899658001959"
- },
- "_npmUser": {
- "name": "dougwilson",
- "email": "doug@somethingdoug.com"
- },
- "_npmVersion": "2.15.11",
"_phantomChildren": {},
"_requested": {
- "raw": "etag@~1.8.0",
- "scope": null,
- "escapedName": "etag",
+ "type": "version",
+ "registry": true,
+ "raw": "etag@1.8.1",
"name": "etag",
- "rawSpec": "~1.8.0",
- "spec": ">=1.8.0 <1.9.0",
- "type": "range"
+ "escapedName": "etag",
+ "rawSpec": "1.8.1",
+ "saveSpec": null,
+ "fetchSpec": "1.8.1"
},
"_requiredBy": [
"/express",
"/send"
],
- "_resolved": "https://registry.npmjs.org/etag/-/etag-1.8.0.tgz",
- "_shasum": "6f631aef336d6c46362b51764044ce216be3c051",
- "_shrinkwrap": null,
- "_spec": "etag@~1.8.0",
- "_where": "/Users/muammar/github/mkchromecast/nodejs/node_modules/express",
+ "_resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
+ "_spec": "1.8.1",
+ "_where": "/Users/muammar/github/mkchromecast/nodejs",
"bugs": {
"url": "https://github.com/jshttp/etag/issues"
},
@@ -59,24 +41,21 @@
"email": "david.bjorklund@gmail.com"
}
],
- "dependencies": {},
"description": "Create simple HTTP ETags",
"devDependencies": {
"beautify-benchmark": "0.2.4",
- "benchmark": "2.1.3",
- "eslint": "3.15.0",
- "eslint-config-standard": "6.2.1",
- "eslint-plugin-markdown": "1.0.0-beta.3",
- "eslint-plugin-promise": "3.4.2",
- "eslint-plugin-standard": "2.0.1",
+ "benchmark": "2.1.4",
+ "eslint": "3.19.0",
+ "eslint-config-standard": "10.2.1",
+ "eslint-plugin-import": "2.7.0",
+ "eslint-plugin-markdown": "1.0.0-beta.6",
+ "eslint-plugin-node": "5.1.1",
+ "eslint-plugin-promise": "3.5.0",
+ "eslint-plugin-standard": "3.0.1",
"istanbul": "0.4.5",
"mocha": "1.21.5",
- "seedrandom": "2.4.2"
- },
- "directories": {},
- "dist": {
- "shasum": "6f631aef336d6c46362b51764044ce216be3c051",
- "tarball": "https://registry.npmjs.org/etag/-/etag-1.8.0.tgz"
+ "safe-buffer": "5.1.1",
+ "seedrandom": "2.4.3"
},
"engines": {
"node": ">= 0.6"
@@ -87,7 +66,6 @@
"README.md",
"index.js"
],
- "gitHead": "16979f788efa8c793c8d07543b4d6aef3d2bfff8",
"homepage": "https://github.com/jshttp/etag#readme",
"keywords": [
"etag",
@@ -95,15 +73,7 @@
"res"
],
"license": "MIT",
- "maintainers": [
- {
- "name": "dougwilson",
- "email": "doug@somethingdoug.com"
- }
- ],
"name": "etag",
- "optionalDependencies": {},
- "readme": "ERROR: No README data found!",
"repository": {
"type": "git",
"url": "git+https://github.com/jshttp/etag.git"
@@ -115,5 +85,5 @@
"test-cov": "istanbul cover node_modules/mocha/bin/_mocha -- --reporter dot --check-leaks test/",
"test-travis": "istanbul cover node_modules/mocha/bin/_mocha --report lcovonly -- --reporter spec --check-leaks test/"
},
- "version": "1.8.0"
+ "version": "1.8.1"
}
diff --git a/nodejs/node_modules/express/History.md b/nodejs/node_modules/express/History.md
index 5bfd5690a..fbf59a234 100644
--- a/nodejs/node_modules/express/History.md
+++ b/nodejs/node_modules/express/History.md
@@ -1,3 +1,129 @@
+4.16.2 / 2017-10-09
+===================
+
+ * Fix `TypeError` in `res.send` when given `Buffer` and `ETag` header set
+ * perf: skip parsing of entire `X-Forwarded-Proto` header
+
+4.16.1 / 2017-09-29
+===================
+
+ * deps: send@0.16.1
+ * deps: serve-static@1.13.1
+ - Fix regression when `root` is incorrectly set to a file
+ - deps: send@0.16.1
+
+4.16.0 / 2017-09-28
+===================
+
+ * Add `"json escape"` setting for `res.json` and `res.jsonp`
+ * Add `express.json` and `express.urlencoded` to parse bodies
+ * Add `options` argument to `res.download`
+ * Improve error message when autoloading invalid view engine
+ * Improve error messages when non-function provided as middleware
+ * Skip `Buffer` encoding when not generating ETag for small response
+ * Use `safe-buffer` for improved Buffer API
+ * deps: accepts@~1.3.4
+ - deps: mime-types@~2.1.16
+ * deps: content-type@~1.0.4
+ - perf: remove argument reassignment
+ - perf: skip parameter parsing when no parameters
+ * deps: etag@~1.8.1
+ - perf: replace regular expression with substring
+ * deps: finalhandler@1.1.0
+ - Use `res.headersSent` when available
+ * deps: parseurl@~1.3.2
+ - perf: reduce overhead for full URLs
+ - perf: unroll the "fast-path" `RegExp`
+ * deps: proxy-addr@~2.0.2
+ - Fix trimming leading / trailing OWS in `X-Forwarded-For`
+ - deps: forwarded@~0.1.2
+ - deps: ipaddr.js@1.5.2
+ - perf: reduce overhead when no `X-Forwarded-For` header
+ * deps: qs@6.5.1
+ - Fix parsing & compacting very deep objects
+ * deps: send@0.16.0
+ - Add 70 new types for file extensions
+ - Add `immutable` option
+ - Fix missing `