Cleanups
No files matched your search
@@ -1,16 +1,16 @@
|
|||||||
# Install
|
# Install
|
||||||
apt install python-virtualenv libmysqlclient-dev
|
apt install python-virtualenv libmysqlclient-dev
|
||||||
virtualenv venv
|
pyvenv venv
|
||||||
. venv/bin/activate
|
. venv/bin/activate
|
||||||
pip install -r requirements.txt
|
pip install -r requirements.txt
|
||||||
python manage.py migrate
|
python manage.py migrate
|
||||||
python manage.py runserver
|
python manage.py loaddata testdata
|
||||||
|
|
||||||
* You will also need a LastFM account and a API account from http://www.last.fm/api/account
|
* You will also need a LastFM account and a API account from http://www.last.fm/api/account
|
||||||
* Create a local\_settings.py and set your own LASTFM\_APIKEY and LASTFM\_API\_SECRET
|
* Create a local\_settings.py and set your own LASTFM\_APIKEY and LASTFM\_API\_SECRET
|
||||||
|
|
||||||
# Usage
|
# Usage
|
||||||
* Create an admin user: python manage.py createsuperuser
|
- Create an admin user: python manage.py createsuperuser
|
||||||
* Goto /admin, login, then add a LastFMUser and make it active.
|
- Goto /admin, login, then add a LastFMUser and make it active.
|
||||||
* Setup your music player (f.ex Spotify) to scrobble to that LastFM account.
|
- Setup your music player (f.ex Spotify) to scrobble to that LastFM account.
|
||||||
* Goto / and you should be set :=)
|
- Goto / and you should be set :=)
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
from django.utils import timezone
|
||||||
|
from rest_framework.response import Response
|
||||||
|
from rest_framework.views import APIView
|
||||||
|
from django.contrib.humanize.templatetags.humanize import naturaltime
|
||||||
|
from dateutil import parser
|
||||||
|
|
||||||
|
from oliver_screen import lastfm, utils
|
||||||
|
|
||||||
|
|
||||||
|
class LastTrackView(APIView):
|
||||||
|
def _get_playback_date(self, playback_date_string):
|
||||||
|
playback_date = parser.parse(playback_date_string)
|
||||||
|
return timezone.make_aware(playback_date, timezone.UTC()) # Last.fm API is in UTC
|
||||||
|
|
||||||
|
def _time_since(self, playback_date_string):
|
||||||
|
playback_date = self._get_playback_date(playback_date_string)
|
||||||
|
formatted = naturaltime(playback_date)
|
||||||
|
formatted = formatted.replace(u'\xa0', u' ') # remove non-breaking space (in latin1)
|
||||||
|
return formatted
|
||||||
|
|
||||||
|
def get(self, request, format=None):
|
||||||
|
user = lastfm.get_user()
|
||||||
|
playedtracks = user.get_recent_tracks(limit=2)
|
||||||
|
|
||||||
|
track_data = {
|
||||||
|
'artist': playedtracks[0].track.get_artist().get_name(),
|
||||||
|
'title': playedtracks[0].track.get_title(),
|
||||||
|
'playback_date': self._get_playback_date(playedtracks[0].playback_date),
|
||||||
|
'time_since': self._time_since(playedtracks[0].playback_date)
|
||||||
|
}
|
||||||
|
return Response({'track': track_data})
|
||||||
|
|
||||||
|
|
||||||
|
class CurrentTrackView(APIView):
|
||||||
|
def get(self, request, format=None):
|
||||||
|
user = lastfm.get_user()
|
||||||
|
track = user.get_now_playing()
|
||||||
|
|
||||||
|
if track is None:
|
||||||
|
# No response from LastFM?
|
||||||
|
return Response({'track': utils.get_current_from_cache()})
|
||||||
|
|
||||||
|
artist = track.get_artist()
|
||||||
|
video = utils.get_youtubevideo(artist.get_name(), track.get_title())
|
||||||
|
image = artist.get_cover_image(size=4)
|
||||||
|
|
||||||
|
now_playing = {
|
||||||
|
'artist': artist.get_name(),
|
||||||
|
'title': track.get_title(),
|
||||||
|
'image': image,
|
||||||
|
'video': video
|
||||||
|
}
|
||||||
|
utils.cache_current(now_playing)
|
||||||
|
|
||||||
|
return Response({'track': now_playing})
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
from django.apps import AppConfig
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
|
||||||
|
class OliveScreenConfig(AppConfig):
|
||||||
|
name = 'oliver_screen'
|
||||||
|
verbose_name = _('Oliver Screen')
|
||||||
|
|
||||||
|
def ready(self):
|
||||||
|
pass
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
import pylast
|
||||||
|
from django.conf import settings
|
||||||
|
from oliver_screen.models import LastFMUser
|
||||||
|
|
||||||
|
|
||||||
|
def get_user():
|
||||||
|
network = pylast.LastFMNetwork(api_key=settings.LASTFM_API_KEY, api_secret=settings.LASTFM_API_SECRET)
|
||||||
|
lastfmusers = LastFMUser.objects.filter(active=True)
|
||||||
|
if len(lastfmusers) != 1:
|
||||||
|
# default
|
||||||
|
return network.get_user(settings.LASTFM_DEFAULT_USERNAME)
|
||||||
|
|
||||||
|
return network.get_user(lastfmusers[0].name)
|
||||||
@@ -7,7 +7,7 @@ class CurrentTrack(models.Model):
|
|||||||
image = models.CharField(max_length=200)
|
image = models.CharField(max_length=200)
|
||||||
retries = models.IntegerField()
|
retries = models.IntegerField()
|
||||||
|
|
||||||
def __unicode__(self):
|
def __str__(self):
|
||||||
return self.title + " - " + self.artist
|
return self.title + " - " + self.artist
|
||||||
|
|
||||||
|
|
||||||
@@ -15,7 +15,7 @@ class LastFMUser(models.Model):
|
|||||||
name = models.CharField(max_length=200)
|
name = models.CharField(max_length=200)
|
||||||
active = models.BooleanField(default=True)
|
active = models.BooleanField(default=True)
|
||||||
|
|
||||||
def __unicode__(self):
|
def __str__(self):
|
||||||
if self.active:
|
if self.active:
|
||||||
return self.name + ": active"
|
return self.name + ": active"
|
||||||
return self.name + ": NOT active"
|
return self.name + ": NOT active"
|
||||||
@@ -28,7 +28,7 @@ class YouTubeVideo(models.Model):
|
|||||||
start = models.IntegerField()
|
start = models.IntegerField()
|
||||||
active = models.BooleanField(default=True)
|
active = models.BooleanField(default=True)
|
||||||
|
|
||||||
def __unicode__(self):
|
def __str__(self):
|
||||||
if self.active:
|
if self.active:
|
||||||
return self.artist + " - " + self.title + ": active"
|
return self.artist + " - " + self.title + ": active"
|
||||||
return self.artist + " - " + self.title + " : NOT active"
|
return self.artist + " - " + self.title + " : NOT active"
|
||||||
|
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 2.8 KiB |
|
Before Width: | Height: | Size: 3.0 KiB After Width: | Height: | Size: 3.0 KiB |
|
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 2.4 KiB |
|
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 2.9 KiB |
|
Before Width: | Height: | Size: 141 KiB After Width: | Height: | Size: 141 KiB |
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 673 B After Width: | Height: | Size: 673 B |
@@ -1,18 +0,0 @@
|
|||||||
/*!
|
|
||||||
* jQuery JavaScript Library v1.6.1
|
|
||||||
* http://jquery.com/
|
|
||||||
*
|
|
||||||
* Copyright 2011, John Resig
|
|
||||||
* Dual licensed under the MIT or GPL Version 2 licenses.
|
|
||||||
* http://jquery.org/license
|
|
||||||
*
|
|
||||||
* Includes Sizzle.js
|
|
||||||
* http://sizzlejs.com/
|
|
||||||
* Copyright 2011, The Dojo Foundation
|
|
||||||
* Released under the MIT, BSD, and GPL Licenses.
|
|
||||||
*
|
|
||||||
* Date: Thu May 12 15:04:36 2011 -0400
|
|
||||||
*/
|
|
||||||
(function(a,b){function cy(a){return f.isWindow(a)?a:a.nodeType===9?a.defaultView||a.parentWindow:!1}function cv(a){if(!cj[a]){var b=f("<"+a+">").appendTo("body"),d=b.css("display");b.remove();if(d==="none"||d===""){ck||(ck=c.createElement("iframe"),ck.frameBorder=ck.width=ck.height=0),c.body.appendChild(ck);if(!cl||!ck.createElement)cl=(ck.contentWindow||ck.contentDocument).document,cl.write("<!doctype><html><body></body></html>");b=cl.createElement(a),cl.body.appendChild(b),d=f.css(b,"display"),c.body.removeChild(ck)}cj[a]=d}return cj[a]}function cu(a,b){var c={};f.each(cp.concat.apply([],cp.slice(0,b)),function(){c[this]=a});return c}function ct(){cq=b}function cs(){setTimeout(ct,0);return cq=f.now()}function ci(){try{return new a.ActiveXObject("Microsoft.XMLHTTP")}catch(b){}}function ch(){try{return new a.XMLHttpRequest}catch(b){}}function cb(a,c){a.dataFilter&&(c=a.dataFilter(c,a.dataType));var d=a.dataTypes,e={},g,h,i=d.length,j,k=d[0],l,m,n,o,p;for(g=1;g<i;g++){if(g===1)for(h in a.converters)typeof h=="string"&&(e[h.toLowerCase()]=a.converters[h]);l=k,k=d[g];if(k==="*")k=l;else if(l!=="*"&&l!==k){m=l+" "+k,n=e[m]||e["* "+k];if(!n){p=b;for(o in e){j=o.split(" ");if(j[0]===l||j[0]==="*"){p=e[j[1]+" "+k];if(p){o=e[o],o===!0?n=p:p===!0&&(n=o);break}}}}!n&&!p&&f.error("No conversion from "+m.replace(" "," to ")),n!==!0&&(c=n?n(c):p(o(c)))}}return c}function ca(a,c,d){var e=a.contents,f=a.dataTypes,g=a.responseFields,h,i,j,k;for(i in g)i in d&&(c[g[i]]=d[i]);while(f[0]==="*")f.shift(),h===b&&(h=a.mimeType||c.getResponseHeader("content-type"));if(h)for(i in e)if(e[i]&&e[i].test(h)){f.unshift(i);break}if(f[0]in d)j=f[0];else{for(i in d){if(!f[0]||a.converters[i+" "+f[0]]){j=i;break}k||(k=i)}j=j||k}if(j){j!==f[0]&&f.unshift(j);return d[j]}}function b_(a,b,c,d){if(f.isArray(b))f.each(b,function(b,e){c||bF.test(a)?d(a,e):b_(a+"["+(typeof e=="object"||f.isArray(e)?b:"")+"]",e,c,d)});else if(!c&&b!=null&&typeof b=="object")for(var e in b)b_(a+"["+e+"]",b[e],c,d);else d(a,b)}function b$(a,c,d,e,f,g){f=f||c.dataTypes[0],g=g||{},g[f]=!0;var h=a[f],i=0,j=h?h.length:0,k=a===bU,l;for(;i<j&&(k||!l);i++)l=h[i](c,d,e),typeof l=="string"&&(!k||g[l]?l=b:(c.dataTypes.unshift(l),l=b$(a,c,d,e,l,g)));(k||!l)&&!g["*"]&&(l=b$(a,c,d,e,"*",g));return l}function bZ(a){return function(b,c){typeof b!="string"&&(c=b,b="*");if(f.isFunction(c)){var d=b.toLowerCase().split(bQ),e=0,g=d.length,h,i,j;for(;e<g;e++)h=d[e],j=/^\+/.test(h),j&&(h=h.substr(1)||"*"),i=a[h]=a[h]||[],i[j?"unshift":"push"](c)}}}function bD(a,b,c){var d=b==="width"?bx:by,e=b==="width"?a.offsetWidth:a.offsetHeight;if(c==="border")return e;f.each(d,function(){c||(e-=parseFloat(f.css(a,"padding"+this))||0),c==="margin"?e+=parseFloat(f.css(a,"margin"+this))||0:e-=parseFloat(f.css(a,"border"+this+"Width"))||0});return e}function bn(a,b){b.src?f.ajax({url:b.src,async:!1,dataType:"script"}):f.globalEval((b.text||b.textContent||b.innerHTML||"").replace(bf,"/*$0*/")),b.parentNode&&b.parentNode.removeChild(b)}function bm(a){f.nodeName(a,"input")?bl(a):a.getElementsByTagName&&f.grep(a.getElementsByTagName("input"),bl)}function bl(a){if(a.type==="checkbox"||a.type==="radio")a.defaultChecked=a.checked}function bk(a){return"getElementsByTagName"in a?a.getElementsByTagName("*"):"querySelectorAll"in a?a.querySelectorAll("*"):[]}function bj(a,b){var c;if(b.nodeType===1){b.clearAttributes&&b.clearAttributes(),b.mergeAttributes&&b.mergeAttributes(a),c=b.nodeName.toLowerCase();if(c==="object")b.outerHTML=a.outerHTML;else if(c!=="input"||a.type!=="checkbox"&&a.type!=="radio"){if(c==="option")b.selected=a.defaultSelected;else if(c==="input"||c==="textarea")b.defaultValue=a.defaultValue}else a.checked&&(b.defaultChecked=b.checked=a.checked),b.value!==a.value&&(b.value=a.value);b.removeAttribute(f.expando)}}function bi(a,b){if(b.nodeType===1&&!!f.hasData(a)){var c=f.expando,d=f.data(a),e=f.data(b,d);if(d=d[c]){var g=d.events;e=e[c]=f.extend({},d);if(g){delete e.handle,e.events={};for(var h in g)for(var i=0,j=g[h].length;i<j;i++)f.event.add(b,h+(g[h][i].namespace?".":"")+g[h][i].namespace,g[h][i],g[h][i].data)}}}}function bh(a,b){return f.nodeName(a,"table")?a.getElementsByTagName("tbody")[0]||a.appendChild(a.ownerDocument.createElement("tbody")):a}function X(a,b,c){b=b||0;if(f.isFunction(b))return f.grep(a,function(a,d){var e=!!b.call(a,d,a);return e===c});if(b.nodeType)return f.grep(a,function(a,d){return a===b===c});if(typeof b=="string"){var d=f.grep(a,function(a){return a.nodeType===1});if(S.test(b))return f.filter(b,d,!c);b=f.filter(b,d)}return f.grep(a,function(a,d){return f.inArray(a,b)>=0===c})}function W(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function O(a,b){return(a&&a!=="*"?a+".":"")+b.replace(A,"`").replace(B,"&")}function N(a){var b,c,d,e,g,h,i,j,k,l,m,n,o,p=[],q=[],r=f._data(this,"events");if(!(a.liveFired===this||!r||!r.live||a.target.disabled||a.button&&a.type==="click")){a.namespace&&(n=new RegExp("(^|\\.)"+a.namespace.split(".").join("\\.(?:.*\\.)?")+"(\\.|$Line truncated
|
|
||||||
)});return}if(e.nodeType===3||e.nodeType===8)return;c.result=b,c.target=e,d=d?f.makeArray(d):[],d.unshift(c);var k=e,l=h.indexOf(":")<0?"on"+h:"";do{var m=f._data(k,"handle");c.currentTarget=k,m&&m.apply(k,d),l&&f.acceptData(k)&&k[l]&&k[l].apply(k,d)===!1&&(c.result=!1,c.preventDefault()),k=k.parentNode||k.ownerDocument||k===c.target.ownerDocument&&a}while(k&&!c.isPropagationStopped());if(!c.isDefaultPrevented()){var n,o=f.event.special[h]||{};if((!o._default||o._default.call(e.ownerDocument,c)===!1)&&(h!=="click"||!f.nodeName(e,"a"))&&f.acceptData(e)){try{l&&e[h]&&(n=e[l],n&&(e[l]=null),f.event.triggered=h,e[h]())}catch(p){}n&&(e[l]=n),f.event.triggered=b}}return c.result}},handle:function(c){c=f.event.fix(c||a.event);var d=((f._data(this,"events")||{})[c.type]||[]).slice(0),e=!c.exclusive&&!c.namespace,g=Array.prototype.slice.call(arguments,0);g[0]=c,c.currentTarget=this;for(var h=0,i=d.length;h<i;h++){var j=d[h];if(e||c.namespace_re.test(j.namespace)){c.handler=j.handler,c.data=j.data,c.handleObj=j;var k=j.handler.apply(this,g);k!==b&&(c.result=k,k===!1&&(c.preventDefault(),c.stopPropagation()));if(c.isImmediatePropagationStopped())break}}return c.result},props:"altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode layerX layerY metaKey newValue offsetX offsetY pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),fix:function(a){if(a[f.expando])return a;var d=a;a=f.Event(d);for(var e=this.props.length,g;e;)g=this.props[--e],a[g]=d[g];a.target||(a.target=a.srcElement||c),a.target.nodeType===3&&(a.target=a.target.parentNode),!a.relatedTarget&&a.fromElement&&(a.relatedTarget=a.fromElement===a.target?a.toElement:a.fromElement);if(a.pageX==null&&a.clientX!=null){var h=a.target.ownerDocument||c,i=h.documentElement,j=h.body;a.pageX=a.clientX+(i&&i.scrollLeft||j&&j.scrollLeft||0)-(i&&i.clientLeft||j&&j.clientLeft||0),a.pageY=a.clientY+(i&&i.scrollTop||j&&j.scrollTop||0)-(i&&i.clientTop||j&&j.clientTop||0)}a.which==null&&(a.charCode!=null||a.keyCode!=null)&&(a.which=a.charCode!=null?a.charCode:a.keyCode),!a.metaKey&&a.ctrlKey&&(a.metaKey=a.ctrlKey),!a.which&&a.button!==b&&(a.which=a.button&1?1:a.button&2?3:a.button&4?2:0);return a},guid:1e8,proxy:f.proxy,special:{ready:{setup:f.bindReady,teardown:f.noop},live:{add:function(a){f.event.add(this,O(a.origType,a.selector),f.extend({},a,{handler:N,guid:a.handler.guid}))},remove:function(a){f.event.remove(this,O(a.origType,a.selector),a)}},beforeunload:{setup:function(a,b,c){f.isWindow(this)&&(this.onbeforeunload=c)},teardown:function(a,b){this.onbeforeunload===b&&(this.onbeforeunload=null)}}}},f.removeEvent=c.removeEventListener?function(a,b,c){a.removeEventListener&&a.removeEventListener(b,c,!1)}:function(a,b,c){a.detachEvent&&a.detachEvent("on"+b,c)},f.Event=function(a,b){if(!this.preventDefault)return new f.Event(a,b);a&&a.type?(this.originalEvent=a,this.type=a.type,this.isDefaultPrevented=a.defaultPrevented||a.returnValue===!1||a.getPreventDefault&&a.getPreventDefault()?F:E):this.type=a,b&&f.extend(this,b),this.timeStamp=f.now(),this[f.expando]=!0},f.Event.prototype={preventDefault:function(){this.isDefaultPrevented=F;var a=this.originalEvent;!a||(a.preventDefault?a.preventDefault():a.returnValue=!1)},stopPropagation:function(){this.isPropagationStopped=F;var a=this.originalEvent;!a||(a.stopPropagation&&a.stopPropagation(),a.cancelBubble=!0)},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=F,this.stopPropagation()},isDefaultPrevented:E,isPropagationStopped:E,isImmediatePropagationStopped:E};var G=function(a){var b=a.relatedTarget;a.type=a.data;try{if(b&&b!==c&&!b.parentNode)return;while(b&&b!==this)b=b.parentNode;b!==this&&f.event.handle.apply(this,arguments)}catch(d){}},H=function(a){a.type=a.data,f.event.handle.apply(this,arguments)};f.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(a,b){f.event.special[a]={setup:function(c){f.event.add(this,b,c&&c.selector?H:G,a)},teardown:function(a){f.event.remove(this,b,a&&a.selector?H:G)}}}),f.support.submitBubbles||(f.event.special.submit={setup:function(a,b){if(!f.nodeName(this,"form"))f.event.add(this,"click.specialSubmit",function(a){var b=a.target,c=b.type;(c==="submit"||c==="image")&&f(b).closest("form").length&&L("submit",this,arguments)}),f.event.add(this,"keypress.specialSubmit",function(a){var b=a.target,c=b.type;(c==="text"||c==="password")&&f(b).closest("form").length&&a.keyCode===13&&L("submit",this,arguments)});else return!1},teardown:function(a){f.event.remove(this,".specialSubmit")}});if(!f.support.changeBubbles){var I,J=function(a){var b=a.type,c=a.value;b==="radio"||b==="checkbox"?c=a.checked:b==="select-multiple"?c=a.selectedIndex>-1?f.map(a.options,function(a){return a.selected}).join("-"):"":f.nodeName(a,"select")&&(c=a.selectedIndex);return c},K=function(c){var d=c.target,e,g;if(!!z.test(d.nLine truncated
|
|
||||||
b[0]&&b[0].ownerDocument||c);var h=[],i;for(var j=0,k;(k=a[j])!=null;j++){typeof k=="number"&&(k+="");if(!k)continue;if(typeof k=="string")if(!bb.test(k))k=b.createTextNode(k);else{k=k.replace($,"<$1></$2>");var l=(_.exec(k)||["",""])[1].toLowerCase(),m=bg[l]||bg._default,n=m[0],o=b.createElement("div");o.innerHTML=m[1]+k+m[2];while(n--)o=o.lastChild;if(!f.support.tbody){var p=ba.test(k),q=l==="table"&&!p?o.firstChild&&o.firstChild.childNodes:m[1]==="<table>"&&!p?o.childNodes:[];for(i=q.length-1;i>=0;--i)f.nodeName(q[i],"tbody")&&!q[i].childNodes.length&&q[i].parentNode.removeChild(q[i])}!f.support.leadingWhitespace&&Z.test(k)&&o.insertBefore(b.createTextNode(Z.exec(k)[0]),o.firstChild),k=o.childNodes}var r;if(!f.support.appendChecked)if(k[0]&&typeof (r=k.length)=="number")for(i=0;i<r;i++)bm(k[i]);else bm(k);k.nodeType?h.push(k):h=f.merge(h,k)}if(d){g=function(a){return!a.type||be.test(a.type)};for(j=0;h[j];j++)if(e&&f.nodeName(h[j],"script")&&(!h[j].type||h[j].type.toLowerCase()==="text/javascript"))e.push(h[j].parentNode?h[j].parentNode.removeChild(h[j]):h[j]);else{if(h[j].nodeType===1){var s=f.grep(h[j].getElementsByTagName("script"),g);h.splice.apply(h,[j+1,0].concat(s))}d.appendChild(h[j])}}return h},cleanData:function(a){var b,c,d=f.cache,e=f.expando,g=f.event.special,h=f.support.deleteExpando;for(var i=0,j;(j=a[i])!=null;i++){if(j.nodeName&&f.noData[j.nodeName.toLowerCase()])continue;c=j[f.expando];if(c){b=d[c]&&d[c][e];if(b&&b.events){for(var k in b.events)g[k]?f.event.remove(j,k):f.removeEvent(j,k,b.handle);b.handle&&(b.handle.elem=null)}h?delete j[f.expando]:j.removeAttribute&&j.removeAttribute(f.expando),delete d[c]}}}});var bo=/alpha\([^)]*\)/i,bp=/opacity=([^)]*)/,bq=/-([a-z])/ig,br=/([A-Z]|^ms)/g,bs=/^-?\d+(?:px)?$/i,bt=/^-?\d/,bu=/^[+\-]=/,bv=/[^+\-\.\de]+/g,bw={position:"absolute",visibility:"hidden",display:"block"},bx=["Left","Right"],by=["Top","Bottom"],bz,bA,bB,bC=function(a,b){return b.toUpperCase()};f.fn.css=function(a,c){if(arguments.length===2&&c===b)return this;return f.access(this,a,c,!0,function(a,c,d){return d!==b?f.style(a,c,d):f.css(a,c)})},f.extend({cssHooks:{opacity:{get:function(a,b){if(b){var c=bz(a,"opacity","opacity");return c===""?"1":c}return a.style.opacity}}},cssNumber:{zIndex:!0,fontWeight:!0,opacity:!0,zoom:!0,lineHeight:!0,widows:!0,orphans:!0},cssProps:{"float":f.support.cssFloat?"cssFloat":"styleFloat"},style:function(a,c,d,e){if(!!a&&a.nodeType!==3&&a.nodeType!==8&&!!a.style){var g,h,i=f.camelCase(c),j=a.style,k=f.cssHooks[i];c=f.cssProps[i]||i;if(d===b){if(k&&"get"in k&&(g=k.get(a,!1,e))!==b)return g;return j[c]}h=typeof d;if(h==="number"&&isNaN(d)||d==null)return;h==="string"&&bu.test(d)&&(d=+d.replace(bv,"")+parseFloat(f.css(a,c))),h==="number"&&!f.cssNumber[i]&&(d+="px");if(!k||!("set"in k)||(d=k.set(a,d))!==b)try{j[c]=d}catch(l){}}},css:function(a,c,d){var e,g;c=f.camelCase(c),g=f.cssHooks[c],c=f.cssProps[c]||c,c==="cssFloat"&&(c="float");if(g&&"get"in g&&(e=g.get(a,!0,d))!==b)return e;if(bz)return bz(a,c)},swap:function(a,b,c){var d={};for(var e in b)d[e]=a.style[e],a.style[e]=b[e];c.call(a);for(e in b)a.style[e]=d[e]},camelCase:function(a){return a.replace(bq,bC)}}),f.curCSS=f.css,f.each(["height","width"],function(a,b){f.cssHooks[b]={get:function(a,c,d){var e;if(c){a.offsetWidth!==0?e=bD(a,b,d):f.swap(a,bw,function(){e=bD(a,b,d)});if(e<=0){e=bz(a,b,b),e==="0px"&&bB&&(e=bB(a,b,b));if(e!=null)return e===""||e==="auto"?"0px":e}if(e<0||e==null){e=a.style[b];return e===""||e==="auto"?"0px":e}return typeof e=="string"?e:e+"px"}},set:function(a,b){if(!bs.test(b))return b;b=parseFloat(b);if(b>=0)return b+"px"}}}),f.support.opacity||(f.cssHooks.opacity={get:function(a,b){return bp.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?parseFloat(RegExp.$1)/100+"":b?"1":""},set:function(a,b){var c=a.style,d=a.currentStyle;c.zoom=1;var e=f.isNaN(b)?"":"alpha(opacity="+b*100+")",g=d&&d.filter||c.filter||"";c.filter=bo.test(g)?g.replace(bo,e):g+" "+e}}),f(function(){f.support.reliableMarginRight||(f.cssHooks.marginRight={get:function(a,b){var c;f.swap(a,{display:"inline-block"},function(){b?c=bz(a,"margin-right","marginRight"):c=a.style.marginRight});return c}})}),c.defaultView&&c.defaultView.getComputedStyle&&(bA=function(a,c){var d,e,g;c=c.replace(br,"-$1").toLowerCase();if(!(e=a.ownerDocument.defaultView))return b;if(g=e.getComputedStyle(a,null))d=g.getPropertyValue(c),d===""&&!f.contains(a.ownerDocument.documentElement,a)&&(d=f.style(a,c));return d}),c.documentElement.currentStyle&&(bB=function(a,b){var c,d=a.currentStyle&&a.currentStyle[b],e=a.runtimeStyle&&a.runtimeStyle[b],f=a.style;!bs.test(d)&&bt.test(d)&&(c=f.left,e&&(a.runtimeStyle.left=a.currentStyle.left),f.left=b==="fontSize"?"1em":d||0,d=f.pixelLeft+"px",f.left=c,e&&(a.runtimeStyle.left=e));return d===""?"auto":d}),bz=bA||bB,f.expr&&f.expr.filters&&(f.expr.filters.hidden=function(a){var b=a.offsetWidth,c=a.offsetHeight;return b===0&&c===0||!f.support.reliableHiddenOffsets&&(a.style.displayLine truncated
|
|
||||||
@@ -1,138 +0,0 @@
|
|||||||
/**
|
|
||||||
* jQuery.timers - Timer abstractions for jQuery
|
|
||||||
* Written by Blair Mitchelmore (blair DOT mitchelmore AT gmail DOT com)
|
|
||||||
* Licensed under the WTFPL (http://sam.zoy.org/wtfpl/).
|
|
||||||
* Date: 2009/10/16
|
|
||||||
*
|
|
||||||
* @author Blair Mitchelmore
|
|
||||||
* @version 1.2
|
|
||||||
*
|
|
||||||
**/
|
|
||||||
|
|
||||||
jQuery.fn.extend({
|
|
||||||
everyTime: function(interval, label, fn, times) {
|
|
||||||
return this.each(function() {
|
|
||||||
jQuery.timer.add(this, interval, label, fn, times);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
oneTime: function(interval, label, fn) {
|
|
||||||
return this.each(function() {
|
|
||||||
jQuery.timer.add(this, interval, label, fn, 1);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
stopTime: function(label, fn) {
|
|
||||||
return this.each(function() {
|
|
||||||
jQuery.timer.remove(this, label, fn);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
jQuery.extend({
|
|
||||||
timer: {
|
|
||||||
global: [],
|
|
||||||
guid: 1,
|
|
||||||
dataKey: "jQuery.timer",
|
|
||||||
regex: /^([0-9]+(?:\.[0-9]*)?)\s*(.*s)?$/,
|
|
||||||
powers: {
|
|
||||||
// Yeah this is major overkill...
|
|
||||||
'ms': 1,
|
|
||||||
'cs': 10,
|
|
||||||
'ds': 100,
|
|
||||||
's': 1000,
|
|
||||||
'das': 10000,
|
|
||||||
'hs': 100000,
|
|
||||||
'ks': 1000000
|
|
||||||
},
|
|
||||||
timeParse: function(value) {
|
|
||||||
if (value == undefined || value == null)
|
|
||||||
return null;
|
|
||||||
var result = this.regex.exec(jQuery.trim(value.toString()));
|
|
||||||
if (result[2]) {
|
|
||||||
var num = parseFloat(result[1]);
|
|
||||||
var mult = this.powers[result[2]] || 1;
|
|
||||||
return num * mult;
|
|
||||||
} else {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
add: function(element, interval, label, fn, times) {
|
|
||||||
var counter = 0;
|
|
||||||
|
|
||||||
if (jQuery.isFunction(label)) {
|
|
||||||
if (!times)
|
|
||||||
times = fn;
|
|
||||||
fn = label;
|
|
||||||
label = interval;
|
|
||||||
}
|
|
||||||
|
|
||||||
interval = jQuery.timer.timeParse(interval);
|
|
||||||
|
|
||||||
if (typeof interval != 'number' || isNaN(interval) || interval < 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (typeof times != 'number' || isNaN(times) || times < 0)
|
|
||||||
times = 0;
|
|
||||||
|
|
||||||
times = times || 0;
|
|
||||||
|
|
||||||
var timers = jQuery.data(element, this.dataKey) || jQuery.data(element, this.dataKey, {});
|
|
||||||
|
|
||||||
if (!timers[label])
|
|
||||||
timers[label] = {};
|
|
||||||
|
|
||||||
fn.timerID = fn.timerID || this.guid++;
|
|
||||||
|
|
||||||
var handler = function() {
|
|
||||||
if ((++counter > times && times !== 0) || fn.call(element, counter) === false)
|
|
||||||
jQuery.timer.remove(element, label, fn);
|
|
||||||
};
|
|
||||||
|
|
||||||
handler.timerID = fn.timerID;
|
|
||||||
|
|
||||||
if (!timers[label][fn.timerID])
|
|
||||||
timers[label][fn.timerID] = window.setInterval(handler,interval);
|
|
||||||
|
|
||||||
this.global.push( element );
|
|
||||||
|
|
||||||
},
|
|
||||||
remove: function(element, label, fn) {
|
|
||||||
var timers = jQuery.data(element, this.dataKey), ret;
|
|
||||||
|
|
||||||
if ( timers ) {
|
|
||||||
|
|
||||||
if (!label) {
|
|
||||||
for ( label in timers )
|
|
||||||
this.remove(element, label, fn);
|
|
||||||
} else if ( timers[label] ) {
|
|
||||||
if ( fn ) {
|
|
||||||
if ( fn.timerID ) {
|
|
||||||
window.clearInterval(timers[label][fn.timerID]);
|
|
||||||
delete timers[label][fn.timerID];
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for ( var fn in timers[label] ) {
|
|
||||||
window.clearInterval(timers[label][fn]);
|
|
||||||
delete timers[label][fn];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for ( ret in timers[label] ) break;
|
|
||||||
if ( !ret ) {
|
|
||||||
ret = null;
|
|
||||||
delete timers[label];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for ( ret in timers ) break;
|
|
||||||
if ( !ret )
|
|
||||||
jQuery.removeData(element, this.dataKey);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
jQuery(window).bind("unload", function() {
|
|
||||||
jQuery.each(jQuery.timer.global, function(index, item) {
|
|
||||||
jQuery.timer.remove(item);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -0,0 +1,143 @@
|
|||||||
|
function format_last_track(track) {
|
||||||
|
return track.artist + ' - ' + track.title + ' <span class="small">' + track.time_since + ' ago.</span><br />';
|
||||||
|
}
|
||||||
|
|
||||||
|
function format_current_track(track) {
|
||||||
|
if($.isEmptyObject(track)) {
|
||||||
|
return '<img src="'+ staticFiles.notrack +'" /><br />';
|
||||||
|
}
|
||||||
|
|
||||||
|
var html = '';
|
||||||
|
if(track.image) {
|
||||||
|
html += '<img src="' + track.image +'" class="now_playing" /><br />';
|
||||||
|
}
|
||||||
|
html += '<img src="'+ staticFiles.spectrumAnalyzer +'" />' + track.artist + ' - ' + track.title +' ♪♫<br />';
|
||||||
|
|
||||||
|
return html;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Update track and now playing */
|
||||||
|
function get_last_track() {
|
||||||
|
$.getJSON(urls.trackLast, function(data) {
|
||||||
|
$( '#last_track' ).html(format_last_track(data.track));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function get_now_playing() {
|
||||||
|
$.getJSON(urls.trackCurrent, function(data) {
|
||||||
|
$( '#now_playing' ).html(format_current_track(data.track));
|
||||||
|
|
||||||
|
if( is_tetris_song() ) {
|
||||||
|
play_tetris();
|
||||||
|
} else {
|
||||||
|
dont_play_tetris();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/***********
|
||||||
|
* YOUTUBE *
|
||||||
|
***********/
|
||||||
|
|
||||||
|
function other_video_playing(video) {
|
||||||
|
var now_playing_pattern = /video.videoId/;
|
||||||
|
|
||||||
|
return ! now_playing_pattern.test( ytplayer.getVideoUrl() );
|
||||||
|
}
|
||||||
|
|
||||||
|
function playVideo(video) {
|
||||||
|
if(ytplayer) {
|
||||||
|
// start the video (if we have'nt allready)
|
||||||
|
if( other_video_playing(video) ) {
|
||||||
|
// show the player
|
||||||
|
$("#player").show();
|
||||||
|
ytplayer.loadVideoById(video.videoId, video.start , "hd720");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function showOrHidePlayer(track) {
|
||||||
|
if(track.artist == '') {
|
||||||
|
$("#player").delay(1000).hide();
|
||||||
|
} else {
|
||||||
|
if( track.video != '') {
|
||||||
|
$("#player").show();
|
||||||
|
playVideo(track.video);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function onYouTubePlayerAPIReady() {
|
||||||
|
/* Initial player */
|
||||||
|
new YT.Player('player', {
|
||||||
|
width: '1200',
|
||||||
|
height: '675',
|
||||||
|
videoId: 'dQw4w9WgXcQ', // Mr. Astley?
|
||||||
|
playerVars: {
|
||||||
|
controls: 0, // no controls
|
||||||
|
iv_load_policy: 3, // no annotations
|
||||||
|
start: 215 // offset into video (last 2 seconds)
|
||||||
|
},
|
||||||
|
/* Attach to these events. */
|
||||||
|
events: {
|
||||||
|
'onReady': onPlayerReady,
|
||||||
|
'onStateChange': onPlayerStateChange
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The API will call this function when the video player is ready. */
|
||||||
|
function onPlayerReady(event) {
|
||||||
|
ytplayer = event.target;
|
||||||
|
event.target.playVideo();
|
||||||
|
event.target.mute();
|
||||||
|
event.target.setPlaybackQuality("hd720");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The API calls this function when the player's state changes. */
|
||||||
|
function onPlayerStateChange(event) {
|
||||||
|
if (event.data == YT.PlayerState.ENDED) {
|
||||||
|
/* Could do something on this state or others... */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function createYoutubePlayer() {
|
||||||
|
/* Loads the YouTube IFrame Player API code asynchronously. */
|
||||||
|
var tag = document.createElement('script');
|
||||||
|
tag.src = "https://www.youtube.com/player_api";
|
||||||
|
var firstScriptTag = document.getElementsByTagName('script')[0];
|
||||||
|
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*********
|
||||||
|
* Start *
|
||||||
|
*********/
|
||||||
|
/* Creates an <iframe> (and YouTube player) after the API code downloads. */
|
||||||
|
var ytplayer;
|
||||||
|
var urls = {
|
||||||
|
trackLast: '/api/track/last/', // {% url 'track-last' %}
|
||||||
|
trackCurrent: '/api/track/current/' // {% url 'track-current' %}
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Initial update */
|
||||||
|
$( document ).ready( function() {
|
||||||
|
|
||||||
|
get_last_track();
|
||||||
|
get_now_playing();
|
||||||
|
|
||||||
|
// createYoutubePlayer();
|
||||||
|
});
|
||||||
|
|
||||||
|
/* Start timers updating a screen resource. */
|
||||||
|
setInterval(get_last_track, 10000);
|
||||||
|
setInterval(get_now_playing, 10000);
|
||||||
|
|
||||||
|
/* Ajax spinner */
|
||||||
|
$( document )
|
||||||
|
.ajaxStart(function() {
|
||||||
|
$( '#spinner' ).show();
|
||||||
|
})
|
||||||
|
.ajaxStop( function() {
|
||||||
|
$( '#spinner' ).hide();
|
||||||
|
});
|
||||||
@@ -89,7 +89,7 @@ function createABrick()
|
|||||||
|
|
||||||
/* Randomly choose a brick image and assign it to the newly created element */
|
/* Randomly choose a brick image and assign it to the newly created element */
|
||||||
/* FIXME: this url should be dynamic */
|
/* FIXME: this url should be dynamic */
|
||||||
image.src = '/static/brick' + randomInteger(1, 5) + '.png';
|
image.src = '/static/images/brick' + randomInteger(1, 5) + '.png';
|
||||||
|
|
||||||
brickDiv.style.top = "-150px";
|
brickDiv.style.top = "-150px";
|
||||||
|
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
{% load staticfiles %}
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Current track</title>
|
||||||
|
{% block head %}{% endblock %}
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
{% block body %}{% endblock %}
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -1,98 +1,29 @@
|
|||||||
|
{% extends "public/base.html" %}
|
||||||
{% load staticfiles %}
|
{% load staticfiles %}
|
||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<title>Current track</title>
|
|
||||||
<link rel="stylesheet" type="text/css" href="{% static 'screen.css' %}" />
|
|
||||||
<script type="text/javascript" src="{% static 'jquery-1.6.1.min.js' %}"></script>
|
|
||||||
<script type="text/javascript" src="{% static 'jquery.timers-1.2.js' %}"></script>
|
|
||||||
<script type="text/javascript" src="{% static 'falling-pieces.js' %}"></script>
|
|
||||||
<script type="text/javascript">
|
|
||||||
/* Update track and now playing */
|
|
||||||
var get_last_track = function() {
|
|
||||||
$( '#last_track' ).load('{% url 'last-track' %}');
|
|
||||||
};
|
|
||||||
var get_now_playing = function() {
|
|
||||||
$( '#now_playing' ).load('{% url 'now-playing' %}', function() {
|
|
||||||
if( is_tetris_song() ) {
|
|
||||||
play_tetris();
|
|
||||||
} else {
|
|
||||||
dont_play_tetris();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
/* Initial update */
|
|
||||||
$( document ).ready( get_last_track );
|
|
||||||
$( document ).ready( get_now_playing );
|
|
||||||
/* Start timers updating a screen resource. */
|
|
||||||
$( document ).everyTime('10s', "last_track", get_last_track );
|
|
||||||
$( document ).everyTime('10s', "now_playing", get_now_playing );
|
|
||||||
|
|
||||||
/* Ajax spinner */
|
{% block head %}
|
||||||
$( document )
|
<link rel="stylesheet" type="text/css" href="{% static 'styles/screen.css' %}" />
|
||||||
.ajaxStart(function() {
|
<script type="text/javascript" src="https://code.jquery.com/jquery-2.2.2.min.js"></script>
|
||||||
$( '#spinner' ).show();
|
<script type="text/javascript" src="{% static 'scripts/falling-pieces.js' %}"></script>
|
||||||
})
|
<script type="text/javascript" src="{% static 'scripts/app.js' %}"></script>
|
||||||
.ajaxStop( function() {
|
<script>
|
||||||
$( '#spinner' ).hide();
|
var staticFiles = {
|
||||||
});
|
notrack: '{% static 'images/no_music_makes_me_sick_by_sakgaa.jpg' %}',
|
||||||
|
spectrumAnalyzer: '{% static 'images/spectrum-analyzer.gif' %}'
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
/* Loads the YouTube IFrame Player API code asynchronously. */
|
{% block body %}
|
||||||
var tag = document.createElement('script');
|
<div id="spinner"><img src="{% static 'images/spinner.gif' %}"/></div>
|
||||||
tag.src = "https://www.youtube.com/player_api";
|
<div id="container">
|
||||||
var firstScriptTag = document.getElementsByTagName('script')[0];
|
<div id="content">
|
||||||
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
|
<div id="player"></div>
|
||||||
|
<div id="now_playing"></div>
|
||||||
/* Creates an <iframe> (and YouTube player) after the API code downloads. */
|
<div id="last_track"></div>
|
||||||
var ytplayer;
|
|
||||||
function onYouTubePlayerAPIReady() {
|
|
||||||
/* Initial player */
|
|
||||||
new YT.Player('player', {
|
|
||||||
width: '1200',
|
|
||||||
height: '675',
|
|
||||||
videoId: 'dQw4w9WgXcQ', // Mr. Astley?
|
|
||||||
playerVars: {
|
|
||||||
controls: 0, // no controls
|
|
||||||
iv_load_policy: 3, // no annotations
|
|
||||||
start: 215 // offset into video (last 2 seconds)
|
|
||||||
},
|
|
||||||
/* Attach to these events. */
|
|
||||||
events: {
|
|
||||||
'onReady': onPlayerReady,
|
|
||||||
'onStateChange': onPlayerStateChange
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/* The API will call this function when the video player is ready. */
|
|
||||||
function onPlayerReady(event) {
|
|
||||||
ytplayer = event.target;
|
|
||||||
event.target.playVideo();
|
|
||||||
event.target.mute();
|
|
||||||
event.target.setPlaybackQuality("hd720");
|
|
||||||
}
|
|
||||||
|
|
||||||
/* The API calls this function when the player's state changes. */
|
|
||||||
function onPlayerStateChange(event) {
|
|
||||||
if (event.data == YT.PlayerState.ENDED) {
|
|
||||||
/* Could do something on this state or others... */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
<div id="spinner"><img src="{% static 'spinner.gif' %}"/></div>
|
|
||||||
<div id="container">
|
|
||||||
<div id="content">
|
|
||||||
<div id="player"></div>
|
|
||||||
<div id="now_playing"></div>
|
|
||||||
<div id="last_track"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div id="gameContainer">
|
</div>
|
||||||
<div id="brickContainer"></div>
|
<div id="gameContainer">
|
||||||
</div>
|
<div id="brickContainer"></div>
|
||||||
|
</div>
|
||||||
</body>
|
{% endblock %}
|
||||||
</html>
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
{% load staticfiles %}
|
|
||||||
{% if now_playing %}
|
|
||||||
{% if now_playing.video %}
|
|
||||||
<script type="text/javascript">
|
|
||||||
function other_video_playing() {
|
|
||||||
var now_playing_pattern = /{{ now_playing.video.videoId }}/;
|
|
||||||
|
|
||||||
return ! now_playing_pattern.test( ytplayer.getVideoUrl() );
|
|
||||||
}
|
|
||||||
|
|
||||||
function play() {
|
|
||||||
if(ytplayer) {
|
|
||||||
// start the video (if we have'nt allready)
|
|
||||||
if( other_video_playing() ) {
|
|
||||||
// show the player
|
|
||||||
$("#player").show();
|
|
||||||
ytplayer.loadVideoById("{{ now_playing.video.videoId }}", {{ now_playing.video.start }}, "hd720");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
play();
|
|
||||||
</script>
|
|
||||||
{% else %}
|
|
||||||
<script type="text/javascript">
|
|
||||||
$("#player").delay(1000).hide();
|
|
||||||
</script>
|
|
||||||
{% if now_playing.image %}
|
|
||||||
<img src="{{ now_playing.image}}" class="now_playing" /><br />
|
|
||||||
{% endif %}
|
|
||||||
{% endif %}
|
|
||||||
<img src="{% static 'spectrum-analyzer.gif' %}" /> {{ now_playing.artist}} - {{ now_playing.title }} ♪♫<br />
|
|
||||||
{% else %}
|
|
||||||
<script type="text/javascript">
|
|
||||||
$("#player").delay(1000).hide();
|
|
||||||
</script>
|
|
||||||
<img src="{% static 'no_music_makes_me_sick_by_sakgaa.jpg' %}" /><br />
|
|
||||||
:[
|
|
||||||
{% endif %}
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
{% if played %}
|
|
||||||
{{ played.artist }} - {{ played.title }} <span class="small">{{ played.playback_date|timesince:played.utcnow }} ago.</span><br />
|
|
||||||
{% endif %}
|
|
||||||
@@ -1,7 +1,11 @@
|
|||||||
from django.conf.urls import patterns, url
|
from django.conf.urls import url
|
||||||
|
|
||||||
urlpatterns = patterns(
|
from oliver_screen.api import LastTrackView, CurrentTrackView
|
||||||
'oliver_screen.views',
|
from oliver_screen.views import IndexView
|
||||||
url(r'^last/$', 'get_last_track', name='last-track'),
|
|
||||||
url(r'^now_playing/$', 'get_now_playing', name='now-playing'),
|
urlpatterns = [
|
||||||
)
|
url(r'^$', IndexView.as_view(), name='index'),
|
||||||
|
# API
|
||||||
|
url(r'^api/track/last/', LastTrackView.as_view(), name='track-last'),
|
||||||
|
url(r'^api/track/current/', CurrentTrackView.as_view(), name='track-current')
|
||||||
|
]
|
||||||
@@ -1,17 +1,4 @@
|
|||||||
import pylast
|
from oliver_screen.models import CurrentTrack, YouTubeVideo
|
||||||
from django.conf import settings
|
|
||||||
|
|
||||||
from oliver_screen.models import CurrentTrack, LastFMUser, YouTubeVideo
|
|
||||||
|
|
||||||
|
|
||||||
def get_lastfmuser():
|
|
||||||
network = pylast.LastFMNetwork(api_key=settings.LASTFM_API_KEY, api_secret=settings.LASTFM_API_SECRET)
|
|
||||||
lastfmusers = LastFMUser.objects.filter(active=True)
|
|
||||||
if len(lastfmusers) != 1:
|
|
||||||
# default
|
|
||||||
return network.get_user(settings.LASTFM_DEFAULT_USERNAME)
|
|
||||||
|
|
||||||
return network.get_user(lastfmusers[0].name)
|
|
||||||
|
|
||||||
|
|
||||||
def get_youtubevideo(artist, title):
|
def get_youtubevideo(artist, title):
|
||||||
@@ -27,7 +14,7 @@ def cache_current(now_playing):
|
|||||||
# Fetch the one CurrentTrack entry in the db and update it with the current track.
|
# Fetch the one CurrentTrack entry in the db and update it with the current track.
|
||||||
try:
|
try:
|
||||||
current = CurrentTrack.objects.get(pk=1)
|
current = CurrentTrack.objects.get(pk=1)
|
||||||
except:
|
except CurrentTrack.DoesNotExist:
|
||||||
# update
|
# update
|
||||||
current = CurrentTrack(artist="", title="", image="", retries=0)
|
current = CurrentTrack(artist="", title="", image="", retries=0)
|
||||||
|
|
||||||
@@ -41,7 +28,7 @@ def cache_current(now_playing):
|
|||||||
def get_current_from_cache():
|
def get_current_from_cache():
|
||||||
try:
|
try:
|
||||||
current = CurrentTrack.objects.get(pk=1)
|
current = CurrentTrack.objects.get(pk=1)
|
||||||
except:
|
except CurrentTrack.DoesNotExist:
|
||||||
current = CurrentTrack(artist="", title="", image="", retries=0)
|
current = CurrentTrack(artist="", title="", image="", retries=0)
|
||||||
current.save()
|
current.save()
|
||||||
|
|
||||||
|
|||||||
@@ -1,63 +1,23 @@
|
|||||||
from datetime import datetime
|
from django.http import JsonResponse
|
||||||
from django.shortcuts import render
|
from django.views.generic import TemplateView
|
||||||
|
|
||||||
from oliver_screen import utils
|
|
||||||
|
|
||||||
|
|
||||||
def index(request):
|
class IndexView(TemplateView):
|
||||||
return render(request, 'public/index.html')
|
template_name = 'public/index.html'
|
||||||
|
|
||||||
|
|
||||||
def get_last_track(request):
|
|
||||||
user = utils.get_lastfmuser()
|
|
||||||
playedtracks = user.get_recent_tracks(limit=2)
|
|
||||||
playback_date = datetime.strptime(playedtracks[0].playback_date, "%d %b %Y, %H:%M")
|
|
||||||
played = {
|
|
||||||
'artist': playedtracks[0].track.get_artist().get_name(),
|
|
||||||
'title': playedtracks[0].track.get_title(),
|
|
||||||
'playback_date': playback_date,
|
|
||||||
'utcnow': datetime.utcnow() # provide utcnow since lastfm servers are UTC
|
|
||||||
}
|
|
||||||
return render(request, "public/tracks.html", {'played': played})
|
|
||||||
|
|
||||||
|
|
||||||
def get_now_playing(request):
|
|
||||||
user = utils.get_lastfmuser()
|
|
||||||
track = user.get_now_playing()
|
|
||||||
if track is not None:
|
|
||||||
artist = track.get_artist()
|
|
||||||
video = utils.get_youtubevideo(artist.get_name(), track.get_title())
|
|
||||||
image = artist.get_cover_image(size=4)
|
|
||||||
|
|
||||||
if "The Shins" in artist.get_name():
|
|
||||||
image = 'http://nikolark.at.neuf.no/the_shins.jpg'
|
|
||||||
|
|
||||||
now_playing = {
|
|
||||||
'artist': artist.get_name(),
|
|
||||||
'title': track.get_title(),
|
|
||||||
'image': image,
|
|
||||||
'video': video
|
|
||||||
}
|
|
||||||
utils.cache_current(now_playing)
|
|
||||||
else:
|
|
||||||
# No response from LastFM db?
|
|
||||||
now_playing = utils.get_current_from_cache()
|
|
||||||
|
|
||||||
return render(request, "public/now_playing.html", {'now_playing': now_playing})
|
|
||||||
|
|
||||||
|
|
||||||
# Mock views
|
# Mock views
|
||||||
def dummy_get_last_track(request):
|
def dummy_get_last_track(request):
|
||||||
played = {'artist': 'The Album Leaf', 'title': 'Another Day (Revised)'}
|
track = {'artist': 'The Album Leaf', 'title': 'Another Day (Revised)', 'time_since': '10 seconds'}
|
||||||
|
return JsonResponse(track)
|
||||||
return render(request, "public/tracks.html", {'played': played})
|
|
||||||
|
|
||||||
|
|
||||||
def dummy_now_playing(request):
|
def dummy_now_playing(request):
|
||||||
now_playing = {
|
track = {
|
||||||
'artist': 'The Album Leaf',
|
'artist': 'The Album Leaf',
|
||||||
'title': 'Over The Pond',
|
'title': 'Over The Pond',
|
||||||
'image': 'http://userserve-ak.last.fm/serve/_/2857325/The+Album+Leaf+1155442304.jpg'
|
'image': 'http://userserve-ak.last.fm/serve/_/2857325/The+Album+Leaf+1155442304.jpg',
|
||||||
|
"video": ''
|
||||||
}
|
}
|
||||||
|
|
||||||
return render(request, "public/now_playing.html", {'now_playing': now_playing})
|
return JsonResponse({'track': track})
|
||||||
@@ -17,16 +17,17 @@ TEMPLATE_DEBUG = True
|
|||||||
|
|
||||||
ALLOWED_HOSTS = []
|
ALLOWED_HOSTS = []
|
||||||
|
|
||||||
|
|
||||||
# Application definition
|
# Application definition
|
||||||
|
|
||||||
INSTALLED_APPS = (
|
INSTALLED_APPS = (
|
||||||
|
'flat',
|
||||||
'django.contrib.admin',
|
'django.contrib.admin',
|
||||||
'django.contrib.auth',
|
'django.contrib.auth',
|
||||||
'django.contrib.contenttypes',
|
'django.contrib.contenttypes',
|
||||||
'django.contrib.sessions',
|
'django.contrib.sessions',
|
||||||
'django.contrib.messages',
|
'django.contrib.messages',
|
||||||
'django.contrib.staticfiles',
|
'django.contrib.staticfiles',
|
||||||
|
|
||||||
|
'rest_framework',
|
||||||
)
|
)
|
||||||
|
|
||||||
INSTALLED_APPS += (
|
INSTALLED_APPS += (
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
from django.conf.urls import patterns, include, url
|
from django.conf.urls import include, url
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
|
|
||||||
urlpatterns = patterns(
|
from oliver_screen import urls as oliver_screen_urls
|
||||||
'',
|
|
||||||
url(r'^$', 'oliver_screen.views.index', name='index'),
|
urlpatterns = [
|
||||||
url(r'', include('oliver_screen.urls')),
|
url(r'', include(oliver_screen_urls)),
|
||||||
url(r'^admin/', include(admin.site.urls)),
|
url(r'^admin/', include(admin.site.urls)),
|
||||||
)
|
]
|
||||||
@@ -8,7 +8,8 @@ https://docs.djangoproject.com/en/1.7/howto/deployment/wsgi/
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
from django.core.wsgi import get_wsgi_application
|
||||||
|
|
||||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "oliversite.settings")
|
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "oliversite.settings")
|
||||||
|
|
||||||
from django.core.wsgi import get_wsgi_application
|
|
||||||
application = get_wsgi_application()
|
application = get_wsgi_application()
|
||||||
@@ -1,3 +1,6 @@
|
|||||||
Django>=1.8,<1.9
|
Django>=1.8,<1.9
|
||||||
|
djangorestframework==3.3.3
|
||||||
|
django-flat-theme==1.1.3
|
||||||
pylast==1.4.2
|
pylast==1.4.2
|
||||||
mysqlclient==1.3.6
|
mysqlclient==1.3.6
|
||||||
|
python-dateutil==2.5.2
|
||||||