├── .gitignore
├── server.py
├── screenshot.png
├── chrome-extension
├── mpv-logo.png
├── manifest.json
├── options.html
├── options.js
└── background.js
├── fair-use-extension
├── fair-use.png
├── manifest.json
├── options.html
├── options.js
└── background.js
├── mkchromecast-extension
├── mkchromecast-logo.png
├── manifest.json
└── background.js
├── LICENSE
├── setup.py
├── README.md
└── play_with_mpv.py
/.gitignore:
--------------------------------------------------------------------------------
1 | *.pem
2 |
--------------------------------------------------------------------------------
/server.py:
--------------------------------------------------------------------------------
1 | play_with_mpv.py
--------------------------------------------------------------------------------
/screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Thann/play-with-mpv/HEAD/screenshot.png
--------------------------------------------------------------------------------
/chrome-extension/mpv-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Thann/play-with-mpv/HEAD/chrome-extension/mpv-logo.png
--------------------------------------------------------------------------------
/fair-use-extension/fair-use.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Thann/play-with-mpv/HEAD/fair-use-extension/fair-use.png
--------------------------------------------------------------------------------
/mkchromecast-extension/mkchromecast-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Thann/play-with-mpv/HEAD/mkchromecast-extension/mkchromecast-logo.png
--------------------------------------------------------------------------------
/mkchromecast-extension/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Cast with MkChromecast",
3 | "version": "0.0.1",
4 | "manifest_version": 2,
5 | "description": "Casts videos in pages like youtube via MkChromecast",
6 | "homepage_url": "https://github.com/thann/play-with-mpv",
7 | "icons": {
8 | "128": "mkchromecast-logo.png"
9 | },
10 | "page_action": {
11 | "default_icon": "mkchromecast-logo.png",
12 | "default_title": "Cast with MkChromecast"
13 | },
14 | "background": {
15 | "scripts": ["background.js"],
16 | "persistent": false
17 | },
18 | "permissions": [
19 | "activeTab",
20 | "contextMenus",
21 | "http://localhost/"
22 | ]
23 | }
24 |
--------------------------------------------------------------------------------
/chrome-extension/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Play with MPV",
3 | "version": "0.1.0",
4 | "manifest_version": 2,
5 | "description": "Sends videos in pages like youtube to an external MPV player",
6 | "homepage_url": "https://github.com/thann/play-with-mpv",
7 | "icons": {
8 | "128": "mpv-logo.png"
9 | },
10 | "page_action": {
11 | "default_icon": "mpv-logo.png",
12 | "default_title": "Play with MPV"
13 | },
14 | "background": {
15 | "scripts": ["background.js"],
16 | "persistent": false
17 | },
18 | "permissions": [
19 | "storage",
20 | "activeTab",
21 | "contextMenus",
22 | "http://localhost/"
23 | ],
24 | "commands": {
25 | "launch": {
26 | "suggested_key": {
27 | "default": "Ctrl+Space"
28 | },
29 | "description": "Play with MPV"
30 | }
31 | },
32 | "options_ui": {
33 | "page": "options.html",
34 | "open_in_tab": false
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/fair-use-extension/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Fair Use Download",
3 | "version": "0.1.0",
4 | "manifest_version": 2,
5 | "description": "Download videos in pages like youtube for Fair Use purposes.",
6 | "homepage_url": "https://github.com/thann/play-with-mpv",
7 | "icons": {
8 | "128": "fair-use.png"
9 | },
10 | "page_action": {
11 | "default_icon": "fair-use.png",
12 | "default_title": "Fair Use Download"
13 | },
14 | "background": {
15 | "scripts": ["background.js"],
16 | "persistent": false
17 | },
18 | "permissions": [
19 | "storage",
20 | "activeTab",
21 | "contextMenus",
22 | "http://localhost/"
23 | ],
24 | "commands": {
25 | "launch": {
26 | "suggested_key": {
27 | "default": "Ctrl+Shift+Space"
28 | },
29 | "description": "Fair Use"
30 | }
31 | },
32 | "options_ui": {
33 | "page": "options.html",
34 | "open_in_tab": false
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/mkchromecast-extension/background.js:
--------------------------------------------------------------------------------
1 |
2 | // Show on all pages
3 | chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab) {
4 | chrome.pageAction.show(tabId);
5 | });
6 |
7 | // Send current tabs url to MPV server
8 | chrome.pageAction.onClicked.addListener(function(tab){
9 | // console.info("Clicked!", tab.url)
10 | playUrl(tab.url);
11 | });
12 |
13 | function playUrl(url) {
14 | var xhr = new XMLHttpRequest();
15 | xhr.onreadystatechange = handleXHR;
16 | xhr.open("GET", "http://localhost:7531/?cast_url="+url, true);
17 | xhr.send();
18 | }
19 |
20 | function handleXHR() {
21 | // console.log("XHR", arguments)
22 | }
23 |
24 | var cast = chrome.contextMenus.create({
25 | "id": "thann.cast-with-mkchromecast",
26 | "title": "Cast with MkChromecast",
27 | "contexts": ["page", "link", "video", "audio"]
28 | });
29 |
30 | chrome.contextMenus.onClicked.addListener(function(info, tab) {
31 | // console.log("item " + info.menuItemId + " was clicked");
32 | // console.log("info: " + JSON.stringify(info));
33 | // console.log("tab: " + JSON.stringify(tab));
34 |
35 | playUrl(info["linkUrl"] || info["srcUrl"]|| info["pageUrl"])
36 | });
37 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | This is free and unencumbered software released into the public domain.
2 |
3 | Anyone is free to copy, modify, publish, use, compile, sell, or
4 | distribute this software, either in source code form or as a compiled
5 | binary, for any purpose, commercial or non-commercial, and by any
6 | means.
7 |
8 | In jurisdictions that recognize copyright laws, the author or authors
9 | of this software dedicate any and all copyright interest in the
10 | software to the public domain. We make this dedication for the benefit
11 | of the public at large and to the detriment of our heirs and
12 | successors. We intend this dedication to be an overt act of
13 | relinquishment in perpetuity of all present and future rights to this
14 | software under copyright law.
15 |
16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22 | OTHER DEALINGS IN THE SOFTWARE.
23 |
--------------------------------------------------------------------------------
/chrome-extension/options.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Play-With-MPV Options
5 |
6 |
7 |
11 |
12 | Maximum Resolution:
13 |
21 |
22 | Server location:
23 |
24 |
25 | MPV Arguments:
26 |
27 |
28 | (one per line)
29 |
30 |
docs
31 |
32 |
33 |
34 |
35 |
36 |
37 |
40 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/fair-use-extension/options.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Fair Use Options
5 |
6 |
7 |
11 |
12 | Server location:
13 |
14 |
15 | Download location:
16 |
17 |
18 | Youtube-DL Arguments:
19 |
20 |
21 | (one per line)
22 |
23 |
24 |
25 |
26 |
27 |
28 | (Icon credit to Dave Gandy
29 |
30 | 🔗
32 | )
33 |
34 |
35 |
36 |
37 |
38 |
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/chrome-extension/options.js:
--------------------------------------------------------------------------------
1 | // Saves options to chrome.storage
2 |
3 | function save_options() {
4 | const server_url = document.getElementById('server_url').value || null;
5 | const maxheight = document.getElementById('maxheight').value || null;
6 | const mpv_args = document.getElementById('mpv_args').value || null;
7 | chrome.storage.sync.set({
8 | server_url, maxheight, mpv_args
9 | }, function() {
10 | // Update status to let user know options were saved.
11 | const status = document.getElementById('status');
12 | status.textContent = 'Options saved.';
13 | setTimeout(function() {
14 | status.textContent = '';
15 | }, 1000);
16 | });
17 | }
18 |
19 | // Restores select box and checkbox state using the preferences
20 | // stored in chrome.storage.
21 | function restore_options() {
22 | chrome.storage.sync.get({
23 | server_url: null,
24 | maxheight: null,
25 | mpv_args: null,
26 | }, function(opts) {
27 | document.getElementById('server_url').value = opts.server_url;
28 | document.getElementById('maxheight').value = opts.maxheight || '';
29 | document.getElementById('mpv_args').value = opts.mpv_args;
30 |
31 | // TODO: chrome seems to block this.
32 | // Check server connectivity
33 | // const xhr = new XMLHttpRequest();
34 | // xhr.onreadystatechange = function(e) {
35 | // document.getElementById('server_status').textContent = (
36 | // e.currentTarget.status?
37 | // 'Connected!':
38 | // 'Disconected!')
39 | // };
40 | // xhr.open("GET", opts.server_url, true);
41 | // xhr.send();
42 | });
43 | }
44 |
45 | document.addEventListener('DOMContentLoaded', restore_options);
46 | document.getElementById('save').addEventListener('click', save_options);
--------------------------------------------------------------------------------
/fair-use-extension/options.js:
--------------------------------------------------------------------------------
1 | // Saves options to chrome.storage
2 |
3 | function save_options() {
4 | const server_url = document.getElementById('server_url').value || null;
5 | const ytdl_args = document.getElementById('ytdl_args').value || null;
6 | const location = document.getElementById('location').value || null;
7 | chrome.storage.sync.set({
8 | server_url, ytdl_args, location
9 | }, function() {
10 | // Update status to let user know options were saved.
11 | const status = document.getElementById('status');
12 | status.textContent = 'Options saved.';
13 | setTimeout(function() {
14 | status.textContent = '';
15 | }, 1000);
16 | });
17 | }
18 |
19 | // Restores select box and checkbox state using the preferences
20 | // stored in chrome.storage.
21 | function restore_options() {
22 | chrome.storage.sync.get({
23 | server_url: null,
24 | ytdl_args: null,
25 | location: null,
26 | }, function(opts) {
27 | document.getElementById('server_url').value = opts.server_url;
28 | document.getElementById('ytdl_args').value = opts.ytdl_args;
29 | document.getElementById('location').value = opts.location || '';
30 |
31 | // TODO: chrome seems to block this.
32 | // Check server connectivity
33 | // const xhr = new XMLHttpRequest();
34 | // xhr.onreadystatechange = function(e) {
35 | // document.getElementById('server_status').textContent = (
36 | // e.currentTarget.status?
37 | // 'Connected!':
38 | // 'Disconected!')
39 | // };
40 | // xhr.open("GET", opts.server_url, true);
41 | // xhr.send();
42 | });
43 | }
44 |
45 | document.addEventListener('DOMContentLoaded', restore_options);
46 | document.getElementById('save').addEventListener('click', save_options);
47 |
--------------------------------------------------------------------------------
/chrome-extension/background.js:
--------------------------------------------------------------------------------
1 |
2 | // Show on all pages
3 | chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab) {
4 | chrome.pageAction.show(tabId);
5 | });
6 |
7 | // Send current tabs url to MPV server
8 | chrome.pageAction.onClicked.addListener(function(tab){
9 | // console.info("Clicked!", tab.url)
10 | playUrl(tab.url, true);
11 | });
12 |
13 | function playUrl(url, pause) {
14 | chrome.storage.sync.get({
15 | server_url: null,
16 | maxheight: null,
17 | mpv_args: null,
18 | }, function(opts) {
19 | if (!opts.server_url)
20 | opts.server_url = 'http://localhost:7531';
21 | if (opts.mpv_args) {
22 | opts.mpv_args = opts.mpv_args.split(/\n/);
23 | } else {
24 | opts.mpv_args = [];
25 | }
26 | if (opts.maxheight) {
27 | opts.mpv_args.splice(0, 0,
28 | `--ytdl-format=bestvideo[height<=?${opts.maxheight}]+bestaudio/best`);
29 | }
30 | const query = (`?play_url=` + encodeURIComponent(url) + [''].concat(
31 | opts.mpv_args.map(encodeURIComponent)).join('&mpv_args='));
32 |
33 | const xhr = new XMLHttpRequest();
34 | xhr.onreadystatechange = handleXHR;
35 | xhr.open("GET", `${opts.server_url}/${query}`, true);
36 | xhr.send();
37 |
38 | // Pause videos in tab
39 | pause && chrome.tabs.executeScript({code: `
40 | for (const v of document.getElementsByTagName('video')) {
41 | v.pause();
42 | }`
43 | });
44 | });
45 | }
46 |
47 | function handleXHR() {
48 | // console.log("XHR", arguments)
49 | }
50 |
51 | var parent = chrome.contextMenus.create({
52 | "id": "thann.play-with-mpv",
53 | "title": "Play with MPV",
54 | "contexts": ["page", "link", "video", "audio"]
55 | });
56 |
57 | chrome.contextMenus.onClicked.addListener(function(info, tab) {
58 | // console.log("item " + info.menuItemId + " was clicked");
59 | // console.log("info: " + JSON.stringify(info));
60 | // console.log("tab: " + JSON.stringify(tab));
61 |
62 | playUrl(info["linkUrl"] || info["srcUrl"]|| info["pageUrl"], true);
63 | });
64 |
65 | chrome.commands.onCommand.addListener(function(command) {
66 | chrome.tabs.query({ active: true, currentWindow: true }, function(tabs) {
67 | playUrl(tabs[0].url, true);
68 | });
69 | });
70 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | import os
3 | from setuptools import setup, find_packages
4 |
5 | description = "Chrome extension and python server that allows you to play videos in webpages with MPV instead."
6 |
7 | def read(fname):
8 | return open(os.path.join(os.path.dirname(__file__), fname)).read()
9 |
10 | def get_version():
11 | from subprocess import Popen, PIPE
12 | try:
13 | from subprocess import DEVNULL # py3
14 | except ImportError:
15 | import os
16 | DEVNULL = open(os.devnull, 'wb')
17 |
18 | def run(*cmd):
19 | return (Popen(cmd, stderr=DEVNULL, stdout=PIPE)
20 | .communicate()[0].decode('utf8').strip())
21 |
22 | return(run('git', 'describe', '--tags').replace('-','.post',1).replace('-','+',1)
23 | or '0.0.0.post{}+g{}'.format(
24 | run('git', 'rev-list', '--count', 'HEAD'),
25 | run('git', 'rev-parse', '--short', 'HEAD')))
26 |
27 | setup(
28 | name = "play-with-mpv",
29 | version = get_version(),
30 | author = "Jonathan Knapp",
31 | author_email = "jaknapp8@gmail.com",
32 | description = description,
33 | license = "MIT",
34 | keywords = "mpv video play chrome extension",
35 | url = "http://github.com/thann/play-with-mpv",
36 | long_description=read('README.md'),
37 | classifiers=[
38 | "Development Status :: 3 - Alpha",
39 | "Topic :: Utilities",
40 | "License :: OSI Approved :: MIT License",
41 | ],
42 |
43 | py_modules=["play_with_mpv"],
44 | install_requires=['wheel', 'youtube-dl'],
45 | entry_points={
46 | 'gui_scripts': [
47 | 'play-with-mpv=play_with_mpv:start',
48 | ],
49 | },
50 | setup_requires=['wheel', 'install_freedesktop>=0.2.0'],
51 | dependency_links=[
52 | "https://github.com/thann/install_freedesktop/tarball/master#egg=install_freedesktop-0.2.0"
53 | ],
54 | desktop_entries={
55 | 'play-with-mpv': {
56 | 'filename': 'thann.play-with-mpv',
57 | 'Name': 'Play With MPV (server)',
58 | 'Categories': 'AudioVideo;Audio;Video;Player;TV',
59 | 'Comment': description,
60 | 'Icon': 'mpv',
61 | },
62 | },
63 | )
64 |
--------------------------------------------------------------------------------
/fair-use-extension/background.js:
--------------------------------------------------------------------------------
1 |
2 | // Show on all pages
3 | chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab) {
4 | chrome.pageAction.show(tabId);
5 | });
6 |
7 | // Send current tabs url to MPV server
8 | chrome.pageAction.onClicked.addListener(function(tab){
9 | // console.info("Clicked!", tab.url)
10 | fairuseUrl(tab.url, true);
11 | });
12 |
13 | function fairuseUrl(url, pause) {
14 | chrome.storage.sync.get({
15 | server_url: null,
16 | ytdl_args: null,
17 | location: null,
18 | }, function(opts) {
19 | if (!opts.server_url)
20 | opts.server_url = 'http://localhost:7531';
21 | if (opts.ytdl_args) {
22 | opts.ytdl_args = opts.ytdl_args.split(/\n/);
23 | } else {
24 | opts.ytdl_args = [];
25 | }
26 | const params = {
27 | fairuse_url: url,
28 | location: opts.location,
29 | ytdl_args: opts.ytdl_args,
30 | }
31 | // build query-string
32 | const query = Object.entries(params)
33 | .map(([k,v]) => {
34 | k = encodeURIComponent(k);
35 | if ([null, undefined].includes(v)) {
36 | v = '';
37 | } else if (v.map) { // arrays
38 | v = v.map(encodeURIComponent).join(`&${k}=`);
39 | } else {
40 | v = encodeURIComponent(v) ;
41 | }
42 | return `${k}=${v}`;
43 | })
44 | .join('&');
45 | const xhr = new XMLHttpRequest();
46 | xhr.onreadystatechange = handleXHR;
47 | xhr.open("GET", `${opts.server_url}/?${query}`, true);
48 | xhr.send();
49 | });
50 | }
51 |
52 | function handleXHR() {
53 | // console.log("XHR", arguments)
54 | }
55 |
56 | var parent = chrome.contextMenus.create({
57 | "id": "thann.fair-use-download",
58 | "title": "Download video for Fair Use",
59 | "contexts": ["page", "link", "video", "audio"]
60 | });
61 |
62 | chrome.contextMenus.onClicked.addListener(function(info, tab) {
63 | // console.log("item " + info.menuItemId + " was clicked");
64 | // console.log("info: " + JSON.stringify(info));
65 | // console.log("tab: " + JSON.stringify(tab));
66 |
67 | fairuseUrl(info["linkUrl"] || info["srcUrl"]|| info["pageUrl"], true);
68 | });
69 |
70 | chrome.commands.onCommand.addListener(function(command) {
71 | chrome.tabs.query({ active: true, currentWindow: true }, function(tabs) {
72 | fairuseUrl(tabs[0].url, true);
73 | });
74 | });
75 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Play with MPV
2 | Chrome extension and python server that allows you to play videos in webpages with MPV instead.
3 | Works on [hundreds of sites](https://rg3.github.io/youtube-dl/supportedsites.html) thanks to youtube-dl,
4 | and even torrents if you install [peerflix](https://github.com/mafintosh/peerflix).
5 |
6 | ## Installation
7 | 1. Install [MPV](https://mpv.io/installation/)
8 | 2. Install [python 2 or 3](https://www.python.org/downloads/) and [pip](https://pip.pypa.io/en/stable/installing/)
9 | 3. Install [chrome extension](https://chrome.google.com/webstore/detail/play-with-mpv/hahklcmnfgffdlchjigehabfbiigleji)
10 | 4. Run `pip install git+git://github.com/thann/play-with-mpv --user`
11 | 5. Start server by running `play-with-mpv` (or use the Linux _free desktop_ shortcut)
12 |
13 | (optional) Install [fair-use](https://chrome.google.com/webstore/detail/fair-use-download/fhokdginneihphnneihijgbhbdoehjaj) extension.
14 | (optional) Install [peerflix](https://github.com/mafintosh/peerflix) to stream torrents.
15 | (optional) Install [mkchromecast](http://mkchromecast.com/) `pip install git+git://github.com/muammar/mkchromecast --user`
16 | and [extension](https://chrome.google.com/webstore/detail/edeepcccaejnnodlpmcoackkdgaijakg).
17 | (recommended) Install youtube-dl through your package manager for frequent updates.
18 | (Arch Linux) [aur package](https://aur.archlinux.org/packages/play-with-mpv-git) available.
19 |
20 | ## Usage
21 | Right-click [this link](https://www.youtube.com/watch?v=dQw4w9WgXcQ) and select "Play with MPV".
22 | MPV should popup and start playing the video. (Ctrl+Space also works)
23 |
24 | 
25 |
26 | ## Autostart
27 | - Linux: `cp {/usr,~/.local}/share/applications/thann.play-with-mpv.desktop ~/.config/autostart`
28 | - MacOS: [instructions](https://stackoverflow.com/questions/29338066/mac-osx-execute-a-python-script-at-startup)
29 | - Windows [instructions](https://stackoverflow.com/questions/4438020/how-to-start-a-python-file-while-windows-starts)
30 |
31 | ## Protips
32 | MPV is [highly configurable](https://mpv.io/manual/stable/), this is just how I like to use it.
33 |
34 | To start in the corner, have no border, and stay on top: edit `~/.config/mpv/mpv.conf`
35 | ```
36 | ontop=yes
37 | border=no
38 | window-scale=0.4
39 | geometry=100%:100%
40 | ```
41 |
42 | In order to resize the window without borders, add keybinds: edit `~/.config/mpv/input.conf`
43 | ```
44 | ` cycle border
45 | ALT+UP add window-scale 0.05
46 | ALT+DOWN add window-scale -0.05
47 | ```
48 |
--------------------------------------------------------------------------------
/play_with_mpv.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # Plays MPV when instructed to by a chrome extension =]
3 |
4 | import sys
5 | import argparse
6 | from subprocess import Popen
7 |
8 | if sys.version_info[0] < 3: # python 2
9 | import BaseHTTPServer
10 | import urlparse
11 | class CompatibilityMixin:
12 | def send_body(self, msg):
13 | self.wfile.write(msg+'\n')
14 | self.wfile.close()
15 |
16 | else: # python 3
17 | import http.server as BaseHTTPServer
18 | import urllib.parse as urlparse
19 | class CompatibilityMixin:
20 | def send_body(self, msg):
21 | self.wfile.write(bytes(msg+'\n', 'utf-8'))
22 |
23 |
24 | class Handler(BaseHTTPServer.BaseHTTPRequestHandler, CompatibilityMixin):
25 | def respond(self, code, body=None):
26 | self.send_response(code)
27 | self.send_header("Content-type", "text/plain")
28 | self.end_headers()
29 | if body:
30 | self.send_body(body)
31 |
32 | def do_GET(self):
33 | try:
34 | url = urlparse.urlparse(self.path)
35 | query = urlparse.parse_qs(url.query)
36 | except:
37 | query = {}
38 | if query.get('mpv_args'):
39 | print("MPV ARGS:", query.get('mpv_args'))
40 | if "play_url" in query:
41 | urls = str(query["play_url"][0])
42 | if urls.startswith('magnet:') or urls.endswith('.torrent'):
43 | try:
44 | pipe = Popen(['peerflix', '-k', urls, '--', '--force-window'] +
45 | query.get("mpv_args", []))
46 | except FileNotFoundError as e:
47 | missing_bin('peerflix')
48 | else:
49 | try:
50 | pipe = Popen(['mpv', urls, '--force-window'] +
51 | query.get("mpv_args", []))
52 | except FileNotFoundError as e:
53 | missing_bin('mpv')
54 | self.respond(200, "playing...")
55 | elif "cast_url" in query:
56 | urls = str(query["cast_url"][0])
57 | if urls.startswith('magnet:') or urls.endswith('.torrent'):
58 | print(" === WARNING: Casting torrents not yet fully supported!")
59 | try:
60 | with Popen(['mkchromecast', '--video',
61 | '--source-url', 'http://localhost:8888']):
62 | pass
63 | except FileNotFoundError as e:
64 | missing_bin('mkchromecast')
65 | pipe.terminate()
66 | else:
67 | try:
68 | pipe = Popen(['mkchromecast', '--video', '-y', urls])
69 | except FileNotFoundError as e:
70 | missing_bin('mkchromecast')
71 | self.respond(200, "casting...")
72 |
73 | elif "fairuse_url" in query:
74 | urls = str(query["fairuse_url"][0])
75 | location = query.get("location", ['~/Downloads/'])[0]
76 | if "%" not in location:
77 | location += "%(title)s.%(ext)s"
78 | print("downloading ", urls, "to", location)
79 | if urls.startswith('magnet:') or urls.endswith('.torrent'):
80 | msg = " === ERROR: Downloading torrents not yet supported!"
81 | print(msg)
82 | self.respond(400, msg)
83 | else:
84 | try:
85 | pipe = Popen(['youtube-dl', urls, '-o', location] +
86 | query.get('ytdl_args', []))
87 | except FileNotFoundError as e:
88 | missing_bin('youtube-dl')
89 | self.respond(200, "downloading...")
90 | else:
91 | self.respond(400)
92 |
93 |
94 | def missing_bin(bin):
95 | print("======================")
96 | print(f"ERROR: {bin.upper()} does not appear to be installed correctly! please ensure you can launch '{bin}' in the terminal.")
97 | print("======================")
98 |
99 |
100 | def start():
101 | parser = argparse.ArgumentParser(description='Plays MPV when instructed to by a browser extension.', formatter_class=argparse.ArgumentDefaultsHelpFormatter)
102 | parser.add_argument('--port', type=int, default=7531, help='The port to listen on.')
103 | parser.add_argument('--public', action='store_true', help='Accept traffic from other computers.')
104 | args = parser.parse_args()
105 | hostname = '0.0.0.0' if args.public else 'localhost'
106 | httpd = BaseHTTPServer.HTTPServer((hostname, args.port), Handler)
107 | print("serving on {}:{}".format(hostname, args.port))
108 | try:
109 | httpd.serve_forever()
110 | except KeyboardInterrupt:
111 | print(" shutting down...")
112 | httpd.shutdown()
113 |
114 |
115 | if __name__ == '__main__':
116 | start()
117 |
118 |
--------------------------------------------------------------------------------