Skip to content

Commit

Permalink
Remove runtime function, add tests, README updates, improved previous…
Browse files Browse the repository at this point in the history
…/next buttons
  • Loading branch information
tocsinde committed Jul 1, 2018
1 parent 8a9affc commit a4edf4b
Show file tree
Hide file tree
Showing 12 changed files with 121 additions and 83 deletions.
16 changes: 9 additions & 7 deletions README.md
Expand Up @@ -18,17 +18,19 @@ Got YouTube or Vimeo playlists with way too many songs and a thirst for explorin

newsic's backend is based on Python 3 and the [Flask](https://github.com/pallets/flask) framework, while the frontend uses [Plyr](https://github.com/sampotts/plyr) (which comes bundled with this repository). Required Python packages are listed in the [requirements.txt](/requirements.txt).

## Usage
### Optional dependencies

Starting with version 0.3 newsic offers support for caching, compressing and minifying - based on [Flask Caching](https://github.com/sh4nks/flask-caching), [Flask-Compress](https://github.com/libwilliam/flask-compress) and [htmlmin](https://github.com/mankyd/htmlmin). The packages are installable with `pip install -r requirements-performance.txt`. See [config.py.example](/config.py.example) for all available options and module documentation.

For using `.env` and `.flaskenv` newsic supports python-dotenv. Installable with `pip install -r requirements-env.txt` or, more directly: `pip install python-dotenv`.

## Run

1. Clone this repository: `git clone https://github.com/newsic/newsic.git`
2. Create a config.py from config.py.example
2. Go to newsic/newsic and create a config.py from config.py.example
3. Add your API keys ([YouTube](https://developers.google.com/youtube/v3/getting-started), [Vimeo](https://developer.vimeo.com/api)) to config.py
4. Install the minimal requirements with `pip install -r requirements.txt`; you might want to use a virtual environment
5. Start newsic with `python3 newsic.py`, which is now available at [127.0.0.1:5000](http://127.0.0.1:5000)

### Performance and caching

Starting with version 0.3 newsic offers optional support for caching, compressing and minifying - based on [Flask Caching](https://github.com/sh4nks/flask-caching), [Flask-Compress](https://github.com/libwilliam/flask-compress) and [Flask-HTMLmin](https://github.com/hamidfzm/Flask-HTMLmin). The packages are installable with `pip install -r requirements-performance.txt`. See [config.py.example](/config.py.example) for all available options and module documentation.
5. Set newsic as Flask app with `export FLASK_APP=newsic` and start it with `flask run`. The script is now running at [127.0.0.1:5000](http://127.0.0.1:5000)

### Deployment

Expand Down
5 changes: 5 additions & 0 deletions newsic/__init__.py
Expand Up @@ -3,12 +3,15 @@
"""

from flask import Flask
from flask_babel import Babel

def create_app():
app = Flask(__name__)
Babel(app)

with app.app_context():
from . import index, youtube, vimeo, custom, autocomplete
from newsic.functions import not_found

app.register_blueprint(index.bp)
app.register_blueprint(index.bp, url_prefix='/<lang_code>')
Expand All @@ -20,4 +23,6 @@ def create_app():
app.register_blueprint(custom.bp, url_prefix='/<lang_code>')
app.register_blueprint(autocomplete.bp)

app.register_error_handler(404, not_found)

return app
3 changes: 1 addition & 2 deletions newsic/custom.py
Expand Up @@ -121,5 +121,4 @@ def custom_playlist(path):
playlistVideoAmount=len(video_list),
playlistLength=int(float((len(video_list) * int(read_config("SNIPPETLENGTH"))) / 60)),
title=video_list[0][2] + " - " + gettext(u"Custom playlist"),
message=message,
runtime=g.runtime())
message=message)
24 changes: 10 additions & 14 deletions newsic/functions.py
Expand Up @@ -43,10 +43,10 @@ def read_config(value):
if getenv(value) == "False":
return False
return getenv(value)
return app.config[value]
if value in app.config:
return app.config[value]

@app.errorhandler(404)
def four0four(_):
def not_found(_):

"""
Handling of non-available routes
Expand Down Expand Up @@ -196,26 +196,22 @@ def ensure_lang_support():
bodyClass="home",
title=gettext(u"URL not found")), 404


@babel.localeselector
def get_locale():
# use standard language as set in config.py
if g.get('lang_code') in read_config("LANGUAGES"):
return g.get('lang_code', read_config("BABEL_DEFAULT_LOCALE"))
return g.get('lang_code')

# Open Graph/Facebook support
if request.args.get("fb_locale") and request.args.get("fb_locale").partition("_")[0] in read_config("LANGUAGES"):
return request.args.get("fb_locale").partition("_")[0]

# best match (based on user request)
return request.accept_languages.best_match(read_config("LANGUAGES"))

bestmatch = request.accept_languages.best_match(read_config("LANGUAGES"))

@app.before_request
def runtime():
if bestmatch:
return bestmatch

"""
Calculation of runtime
"""

g.starttime = perf_counter()
g.runtime = lambda: "%.5fs" % (perf_counter() - g.starttime)
# fall back to default language
return read_config("BABEL_DEFAULT_LOCALE")
6 changes: 2 additions & 4 deletions newsic/index.py
Expand Up @@ -7,7 +7,7 @@
)

from newsic.functions import (
cache, get_locale, gettext, debug
cache, get_locale, gettext, debug, read_config
)

import re
Expand All @@ -26,9 +26,7 @@ def index():
"index.html",
getlocale=get_locale(),
bodyClass="home",
title=gettext(u"Home"),
runtime=g.runtime()
)
title=gettext(u"Home"))

@bp.route("/", methods=["POST"])
def index_post():
Expand Down
85 changes: 43 additions & 42 deletions newsic/static/js/videohandler.js
Expand Up @@ -178,7 +178,7 @@ var playPrevious = function() {
prevOrNext = "prev";
i--;
jumpTo(i);
updateElements();
//updateElements();
}
}

Expand All @@ -187,7 +187,7 @@ var playNext = function() {
prevOrNext = "next";
i++;
jumpTo(i);
updateElements();
//updateElements();
}
}

Expand All @@ -200,7 +200,7 @@ var searchLyrics = function() {
}

var updateElements = function() {
var index;
var index = 0;

var firstCountdown = document.getElementsByClassName("countdown");
if(firstCountdown.length > 1) firstCountdown[1].classList.toggle("countdown");
Expand All @@ -215,27 +215,32 @@ var updateElements = function() {
document.getElementById("title").innerHTML = title;
document.getElementsByTagName("title")[0].innerHTML = title + " - " + document.getElementById("snippets").dataset.playlisttitle + " - newsic";

index = 0;


// TODO: these elements have already been referenced (lines 110 and 111)
document.getElementsByClassName("searchLyrics")[index].href = "https://genius.com/search?q=" + title.replace(/\s+/g, '+');
document.getElementsByClassName("playMix")[index].href = "/" + snippets[i].dataset.type + "/mix/" + snippets[i].dataset.id + "/";

if(i == (snippets.length - 1)) {
playNextElements[0].classList.add("inactive");
playNextElements[0].setAttribute("disabled", "disabled");
} else {
playNextElements[0].classList.remove("inactive");
playNextElements[0].removeAttribute("disabled");
}

if(i == 0) {
playPreviousElements[0].classList.add("inactive");
playPreviousElements[0].setAttribute("disabled", "disabled");
} else {
playPreviousElements[0].classList.remove("inactive");
playPreviousElements[0].removeAttribute("disabled");
if(playNextElements.item(1)) index = 1;

searchLyricsElements[index].href = "https://genius.com/search?q=" + title.replace(/\s+/g, '+');
playMixElements[index].href = "/" + snippets[i].dataset.type + "/mix/" + snippets[i].dataset.id + "/";

switch(i) {
case snippets.length - 1:
playNextElements[index].classList.add("inactive");
playNextElements[index].setAttribute("disabled", "disabled");
playPreviousElements[index].classList.remove("inactive");
playPreviousElements[index].removeAttribute("disabled", "disabled");
break;

case 0:
playPreviousElements[index].classList.add("inactive");
playPreviousElements[index].setAttribute("disabled", "disabled");
playNextElements[index].classList.remove("inactive");
playNextElements[index].removeAttribute("disabled", "disabled");
break;

default:
playNextElements[index].classList.remove("inactive");
playNextElements[index].removeAttribute("disabled", "disabled");
playPreviousElements[index].classList.remove("inactive");
playPreviousElements[index].removeAttribute("disabled", "disabled");
break;
}
}

Expand All @@ -248,20 +253,20 @@ var jumpTo = function(index) {
provider: snippets[index].dataset.type
}]
};
updateElements();
}

var mindTheHash = function() {
// set index to first array element or - if hash in url is set - to requested item
if (location.hash) {
debugMessage("Hash is set");
var input = window.location.hash.replace("#","");
if(input < snippets.length) i = input;
else i = window.location.hash = snippets.length-1; // if hash > no. of videos
var input = Number(window.location.hash.replace("#",""));
if(input < snippets.length) i = input;
else i = window.location.hash = snippets.length-1; // if hash > no. of videos
} else i = 0; // no hash

debugMessage("This is the beginning of a newsic session, the player is ready. Whoop whoop, fasten your seatbelts, etc.");
if (i == 0) debugMessage("This is the beginning of a newsic session, the player is ready. Whoop whoop, fasten your seatbelts, etc.");
jumpTo(i);
updateElements();
};

var PlyrCustomButton = function(className, before, dataPlyr, fontawesome, labeltext) {
Expand Down Expand Up @@ -322,15 +327,17 @@ video.on("ready", function() {
video.currentTime = parseFloat(snippets[i].getAttribute("data-start"));
}

debugMessage("Jumped to start time.");
debugMessage(snippets[i].dataset.id + " " + snippets[i].dataset.start + " " + snippets[i].dataset.end);
updateElements();

debugMessage("Jumped to start time.");
debugMessage(snippets[i].dataset.id + " " + snippets[i].dataset.start + " " + snippets[i].dataset.end);

// handle newsic's autoplay setting (affects first snippet only)
if(!autoplayFirstVideo && !location.hash && i === 0 && prevOrNext != "prev") {
video.pause();
} else video.play();
// handle newsic's autoplay setting (affects first snippet only)
if(!autoplayFirstVideo && !location.hash && i === 0 && prevOrNext != "prev") {
video.pause();
} else video.play();

if (!muted) video.muted = false;
if (!muted) video.muted = false;
}
);

Expand All @@ -345,13 +352,7 @@ video.on("playing", function() {
elementPlayPause.appendChild(newElement);

if(snippets[i].dataset.type === "vimeo" && ready) {

console.log("playing event");

video.currentTime = parseFloat(snippets[i].getAttribute("data-start"));

debugMessage("Jumped to start time.");
debugMessage(snippets[i].dataset.id + " " + snippets[i].dataset.start + " " + snippets[i].dataset.end);
ready = false;
}
});
Expand Down
2 changes: 1 addition & 1 deletion newsic/static/js/videohandler.min.js

Large diffs are not rendered by default.

2 changes: 0 additions & 2 deletions newsic/templates/base.html
Expand Up @@ -99,8 +99,6 @@
<script defer src="{{ url_for('static', filename='js/fontawesome/fontawesome-5.1.0.js') }}"></script>
<script defer src="{{ url_for('static', filename='js/fontawesome/brands-5.1.0.js') }}"></script>
<script defer src="{{ url_for('static', filename='js/fontawesome/solid-5.1.0.js') }}"></script>

{% if runtime %}<!-- {{ _('runtime') }}: {{ runtime }} -->{%- endif %}
</body>

</html>
10 changes: 2 additions & 8 deletions newsic/vimeo.py
Expand Up @@ -81,8 +81,6 @@ def play_vimeo(vimeo_type, vimeo_id):
data["duration"],
ids.group(2)])

debug(("\nRuntime: {}").format(g.runtime()))

return render_template(
"play.html",
getlocale=get_locale(),
Expand All @@ -92,8 +90,7 @@ def play_vimeo(vimeo_type, vimeo_id):
playlistVideoAmount=len(video_list),
playlistLength=int(float((len(video_list) * int(read_config("SNIPPETLENGTH"))) / 60)),
title=video_list[0][2] + " - " + general_data["name"],
message="vimeo-beta",
runtime=g.runtime())
message="vimeo-beta")

@bp.route("/vimeo/mix/<int:vimeo_id>/")
@cache()
Expand Down Expand Up @@ -131,8 +128,6 @@ def mix_vimeo(vimeo_id):
data["duration"],
ids.group(2)])

debug(("\nRuntime: {}").format(g.runtime()))

return render_template(
"play.html",
getlocale=get_locale(),
Expand All @@ -141,5 +136,4 @@ def mix_vimeo(vimeo_id):
playlistVideoAmount=len(video_list),
playlistLength=int(float((len(video_list) * int(read_config("SNIPPETLENGTH"))) / 60)),
title=video_list[0][2] + " - " + gettext(u"Vimeo mix"),
message="vimeo-beta",
runtime=g.runtime())
message="vimeo-beta")
4 changes: 1 addition & 3 deletions newsic/youtube.py
Expand Up @@ -271,7 +271,6 @@ def play_youtube(youtube_playlist):
# TODO: rename yt_grab
video_list = yt_grab(playlist_id=youtube_playlist)

debug(("\nRuntime: {}").format(g.runtime()))
return render_template(
"play.html",
getlocale=get_locale(),
Expand All @@ -280,8 +279,7 @@ def play_youtube(youtube_playlist):
playlistCreator=general_info["channelTitle"],
playlistVideoAmount=len(video_list),
playlistLength=int(float((len(video_list) * int(read_config("SNIPPETLENGTH"))) / 60)),
title=video_list[0][2] + " - " + general_info["title"],
runtime=g.runtime())
title=video_list[0][2] + " - " + general_info["title"])


@bp.route("/watch/")
Expand Down
14 changes: 14 additions & 0 deletions tests/conftest.py
@@ -0,0 +1,14 @@
import pytest
from newsic import create_app

@pytest.fixture()
def app():
app = create_app()
with app.app_context():
app.config.from_object('newsic.config.Local')
app.config['TESTING'] = True
yield app

@pytest.fixture()
def client(app):
return app.test_client()
33 changes: 33 additions & 0 deletions tests/test_routes.py
@@ -0,0 +1,33 @@
import pytest

@pytest.mark.parametrize("route, expected", [
("/youtube/PLdduFHK2eLvfGM7ADIbCgWHFRvu1yBNj0/", "Now, Now - MJ"),
("/vimeo/album/3951181/", "Rollercoaster Girl")
])
def test_routes(client, route, expected):
response = client.get(route)
assert expected.encode('ascii') in response.data


@pytest.mark.parametrize("route, expected", [
("/youtube/invalid-playlist/test/", "URL not found"),
("/youtube/invalid-playlist/", "This playlist is either empty, private or non-existing"),
("/vimeo/channel/invalid-channel/", "This album/channel is either empty, private or non-existing")
])
def test_routes_i18n_en(client, route, expected, app):
with app.app_context():
app.config['BABEL_DEFAULT_LOCALE'] = 'en'
response = client.get(route)
assert expected.encode('ascii') in response.data


@pytest.mark.parametrize("route, expected", [
("/youtube/invalid-playlist/test/", "URL nicht gefunden"),
("/youtube/invalid-playlist/", "Diese Playlist ist entweder leer, privat oder existiert nicht"),
("/vimeo/channel/invalid-channel/", "Dieses Album bzw. dieser Channel ist entweder leer, privat oder existiert nicht")
])
def test_routes_i18n_de(client, route, expected, app):
with app.app_context():
app.config['BABEL_DEFAULT_LOCALE'] = 'de'
response = client.get(route)
assert expected.encode('ascii') in response.data

0 comments on commit a4edf4b

Please sign in to comment.