<commit_before>#!/usr/bin/env python
#First run the command avahi-publish-service TestConnect _spotify-connect._tcp 4000 VERSION=1.0 CPath=/login/_zeroconf
#TODO: Add error checking
#TODO: Show when request fails on webpage
import os
import argparse
import re
from flask import Flask, request, abort, jsonify, render_template, redirect, flash, url_for
from flask_bootstrap import Bootstrap
from flask.ext.cors import CORS
from gevent.wsgi import WSGIServer
from gevent import spawn_later, sleep
from connect_ffi import ffi, lib
from connect import Connect
from utils import get_zeroconf_vars, get_metadata, get_image_url

web_arg_parser = argparse.ArgumentParser(add_help=False)

#Not a tuple, evaluates the same as "" + ""
cors_help = (
    "enable CORS support for this host (for the web api). "
    "Must be in the format <protocol>://<hostname>:<port>. "
    "Port can be excluded if its 80 (http) or 443 (https). "
    "Can be specified multiple times"
)


def validate_cors_host(host):
    host_regex = re.compile(r'^(http|https)://[a-zA-Z0-9][a-zA-Z0-9-.]+(:[0-9]{1,5})?$')
    result = re.match(host_regex, host)
    if result is None:
        raise argparse.ArgumentTypeError('%s is not in the format <protocol>://<hostname>:<port>. Protocol must be http or https' % host)
    return host

web_arg_parser.add_argument('--cors', help=cors_help, action='append', type=validate_cors_host)
args = web_arg_parser.parse_known_args()[0]

app = Flask(__name__)
Bootstrap(app)
#Add CORS headers to API requests for specified hosts
CORS(app, resources={r"/api/*": {"origins": args.cors}})

#Serve bootstrap files locally instead of from a CDN
app.config['BOOTSTRAP_SERVE_LOCAL'] = True
app.secret_key = os.urandom(24)

#Used by the error callback to determine login status
invalid_login = False

@ffi.callback('void(SpError error, void *userdata)')
def web_error_callback(error, userdata):
    global invalid_login
    if error == lib.kSpErrorLoginBadCredentials:
        invalid_login = True

connect_app = Connect(web_error_callback, web_arg_parser)

if os.environ.get('DEBUG') or connect_app.args.debug:
    app.debug = True

##Routes

#Home page
@app.route('/')
def index():
    return render_template('index.html')

##API routes

#Playback routes
@app.route('/api/playback/play')
def playback_play():
    lib.SpPlaybackPlay()
    return '', 204

@app.route('/api/playback/pause')
def playback_pause():
    lib.SpPlaybackPause()
    return '', 204

@app.route('/api/playback/prev')
def playback_prev():
    lib.SpPlaybackSkipToPrev()
    return '', 204

@app.route('/api/playback/next')
def playback_next():
    lib.SpPlaybackSkipToNext()
    return '', 204

#TODO: Add ability to disable shuffle/repeat
@app.route('/api/playback/shuffle')
def playback_shuffle():
    lib.SpPlaybackEnableShuffle()
    return '', 204

@app.route('/api/playback/repeat')
def playback_repeat():
    lib.SpPlaybackEnableRepeat()
    return '', 204

@app.route('/api/playback/volume', methods=['GET'])
def playback_volume():
    return jsonify({
        'volume': lib.SpPlaybackGetVolume()
    })

@app.route('/api/playback/volume', methods=['POST'], endpoint='playback_volume-post')
def playback_volume():
    volume = int(request.form.get('value'))
    if not volume:
        return jsonify({
            'error': 'value must be set'
        }), 400
    lib.SpPlaybackUpdateVolume(volume)
    return '', 204

#Info routes
@app.route('/api/info/metadata')
def info_metadata():
    res = get_metadata()
    res['volume'] = lib.SpPlaybackGetVolume()
    return jsonify(res)

@app.route('/api/info/status')
def info_status():
    return jsonify({
        'active': bool(lib.SpPlaybackIsActiveDevice()),
        'playing': bool(lib.SpPlaybackIsPlaying()),
        'shuffle': bool(lib.SpPlaybackIsShuffled()),
        'repeat': bool(lib.SpPlaybackIsRepeated()),
        'logged_in': bool(lib.SpConnectionIsLoggedIn())
    })
<commit_msg>Fix bug where volume couldn't be set to 0 through the web interface/api<commit_after>#!/usr/bin/env python
#First run the command avahi-publish-service TestConnect _spotify-connect._tcp 4000 VERSION=1.0 CPath=/login/_zeroconf
#TODO: Add error checking
#TODO: Show when request fails on webpage
import os
import argparse
import re
from flask import Flask, request, abort, jsonify, render_template, redirect, flash, url_for
from flask_bootstrap import Bootstrap
from flask.ext.cors import CORS
from gevent.wsgi import WSGIServer
from gevent import spawn_later, sleep
from connect_ffi import ffi, lib
from connect import Connect
from utils import get_zeroconf_vars, get_metadata, get_image_url

web_arg_parser = argparse.ArgumentParser(add_help=False)

#Not a tuple, evaluates the same as "" + ""
cors_help = (
    "enable CORS support for this host (for the web api). "
    "Must be in the format <protocol>://<hostname>:<port>. "
    "Port can be excluded if its 80 (http) or 443 (https). "
    "Can be specified multiple times"
)


def validate_cors_host(host):
    host_regex = re.compile(r'^(http|https)://[a-zA-Z0-9][a-zA-Z0-9-.]+(:[0-9]{1,5})?$')
    result = re.match(host_regex, host)
    if result is None:
        raise argparse.ArgumentTypeError('%s is not in the format <protocol>://<hostname>:<port>. Protocol must be http or https' % host)
    return host

web_arg_parser.add_argument('--cors', help=cors_help, action='append', type=validate_cors_host)
args = web_arg_parser.parse_known_args()[0]

app = Flask(__name__)
Bootstrap(app)
#Add CORS headers to API requests for specified hosts
CORS(app, resources={r"/api/*": {"origins": args.cors}})

#Serve bootstrap files locally instead of from a CDN
app.config['BOOTSTRAP_SERVE_LOCAL'] = True
app.secret_key = os.urandom(24)

#Used by the error callback to determine login status
invalid_login = False

@ffi.callback('void(SpError error, void *userdata)')
def web_error_callback(error, userdata):
    global invalid_login
    if error == lib.kSpErrorLoginBadCredentials:
        invalid_login = True

connect_app = Connect(web_error_callback, web_arg_parser)

if os.environ.get('DEBUG') or connect_app.args.debug:
    app.debug = True

##Routes

#Home page
@app.route('/')
def index():
    return render_template('index.html')

##API routes

#Playback routes
@app.route('/api/playback/play')
def playback_play():
    lib.SpPlaybackPlay()
    return '', 204

@app.route('/api/playback/pause')
def playback_pause():
    lib.SpPlaybackPause()
    return '', 204

@app.route('/api/playback/prev')
def playback_prev():
    lib.SpPlaybackSkipToPrev()
    return '', 204

@app.route('/api/playback/next')
def playback_next():
    lib.SpPlaybackSkipToNext()
    return '', 204

#TODO: Add ability to disable shuffle/repeat
@app.route('/api/playback/shuffle')
def playback_shuffle():
    lib.SpPlaybackEnableShuffle()
    return '', 204

@app.route('/api/playback/repeat')
def playback_repeat():
    lib.SpPlaybackEnableRepeat()
    return '', 204

@app.route('/api/playback/volume', methods=['GET'])
def playback_volume():
    return jsonify({
        'volume': lib.SpPlaybackGetVolume()
    })

@app.route('/api/playback/volume', methods=['POST'], endpoint='playback_volume-post')
def playback_volume():
    volume = request.form.get('value')
    if volume is None:
        return jsonify({
            'error': 'value must be set'
        }), 400
    lib.SpPlaybackUpdateVolume(int(volume))
    return '', 204

#Info routes
@app.route('/api/info/metadata')
def info_metadata():
    res = get_metadata()
    res['volume'] = lib.SpPlaybackGetVolume()
    return jsonify(res)

@app.route('/api/info/status')
def info_status():
    return jsonify({
        'active': bool(lib.SpPlaybackIsActiveDevice()),
        'playing': bool(lib.SpPlaybackIsPlaying()),
        'shuffle': bool(lib.SpPlaybackIsShuffled()),
        'repeat': bool(lib.SpPlaybackIsRepeated()),
        'logged_in': bool(lib.SpConnectionIsLoggedIn())
    })

{'fraction_non_alphanumeric': 0.13456664977192093, 'fraction_numerical': 0.0117840851495185, 'mean_word_length': 4.387713310580205, 'pattern_counts': {'":': 4, '<': 15, '<?xml version=': 0, '>': 15, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6727532', 'n_tokens_mistral': 2667, 'n_tokens_neox': 2548, 'n_words': 653}