This commit is contained in:
nikolark committed 2016-04-02 15:11:03 +02:00
1 parent 0df0922867
commit 3a3874a8be
30 files changed
+295 -9308

No files matched your search

+6 -6
View File
@@ -1,16 +1,16 @@
# Install
apt install python-virtualenv libmysqlclient-dev
virtualenv venv
pyvenv venv
. venv/bin/activate
pip install -r requirements.txt
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
* Create a local\_settings.py and set your own LASTFM\_APIKEY and LASTFM\_API\_SECRET
# Usage
* Create an admin user: python manage.py createsuperuser
* Goto /admin, login, then add a LastFMUser and make it active.
* Setup your music player (f.ex Spotify) to scrobble to that LastFM account.
* Goto / and you should be set :=)
- Create an admin user: python manage.py createsuperuser
- Goto /admin, login, then add a LastFMUser and make it active.
- Setup your music player (f.ex Spotify) to scrobble to that LastFM account.
- Goto / and you should be set :=)
+55
View File
@@ -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})
+10
View File
@@ -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
File renamed without changes.
+13
View File
@@ -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)
+3 -3
View File
@@ -7,7 +7,7 @@ class CurrentTrack(models.Model):
image = models.CharField(max_length=200)
retries = models.IntegerField()
def __unicode__(self):
def __str__(self):
return self.title + " - " + self.artist
@@ -15,7 +15,7 @@ class LastFMUser(models.Model):
name = models.CharField(max_length=200)
active = models.BooleanField(default=True)
def __unicode__(self):
def __str__(self):
if self.active:
return self.name + ": active"
return self.name + ": NOT active"
@@ -28,7 +28,7 @@ class YouTubeVideo(models.Model):
start = models.IntegerField()
active = models.BooleanField(default=True)
def __unicode__(self):
def __str__(self):
if self.active:
return self.artist + " - " + self.title + ": active"
return self.artist + " - " + self.title + " : NOT active"
File renamed without changes.

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

File renamed without changes.

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

File renamed without changes.

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

File renamed without changes.

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

File renamed without changes.

Before

Width:  |  Height:  |  Size: 141 KiB

After

Width:  |  Height:  |  Size: 141 KiB

File renamed without changes.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

File renamed without changes.

Before

Width:  |  Height:  |  Size: 673 B

After

Width:  |  Height:  |  Size: 673 B

File diff suppressed because it is too large. Load diff
-18
View File
@@ -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
-138
View File
@@ -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);
});
});
+143
View File
@@ -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 */
/* 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";
File renamed without changes.
+12
View File
@@ -0,0 +1,12 @@
{% load staticfiles %}
<!DOCTYPE html>
<html>
<head>
<title>Current track</title>
{% block head %}{% endblock %}
</head>
<body>
{% block body %}{% endblock %}
</body>
</html>
+15 -84
View File
@@ -1,88 +1,21 @@
{% extends "public/base.html" %}
{% 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' %}');
{% block head %}
<link rel="stylesheet" type="text/css" href="{% static 'styles/screen.css' %}" />
<script type="text/javascript" src="https://code.jquery.com/jquery-2.2.2.min.js"></script>
<script type="text/javascript" src="{% static 'scripts/falling-pieces.js' %}"></script>
<script type="text/javascript" src="{% static 'scripts/app.js' %}"></script>
<script>
var staticFiles = {
notrack: '{% static 'images/no_music_makes_me_sick_by_sakgaa.jpg' %}',
spectrumAnalyzer: '{% static 'images/spectrum-analyzer.gif' %}'
};
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 */
$( document )
.ajaxStart(function() {
$( '#spinner' ).show();
})
.ajaxStop( function() {
$( '#spinner' ).hide();
});
/* 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);
/* Creates an <iframe> (and YouTube player) after the API code downloads. */
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>
{% endblock %}
<body>
<div id="spinner"><img src="{% static 'spinner.gif' %}"/></div>
{% block body %}
<div id="spinner"><img src="{% static 'images/spinner.gif' %}"/></div>
<div id="container">
<div id="content">
<div id="player"></div>
@@ -93,6 +26,4 @@
<div id="gameContainer">
<div id="brickContainer"></div>
</div>
</body>
</html>
{% endblock %}
@@ -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 %}
+10 -6
View File
@@ -1,7 +1,11 @@
from django.conf.urls import patterns, url
from django.conf.urls import url
urlpatterns = patterns(
'oliver_screen.views',
url(r'^last/$', 'get_last_track', name='last-track'),
url(r'^now_playing/$', 'get_now_playing', name='now-playing'),
)
from oliver_screen.api import LastTrackView, CurrentTrackView
from oliver_screen.views import IndexView
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')
]
+3 -16
View File
@@ -1,17 +1,4 @@
import pylast
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)
from oliver_screen.models import CurrentTrack, YouTubeVideo
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.
try:
current = CurrentTrack.objects.get(pk=1)
except:
except CurrentTrack.DoesNotExist:
# update
current = CurrentTrack(artist="", title="", image="", retries=0)
@@ -41,7 +28,7 @@ def cache_current(now_playing):
def get_current_from_cache():
try:
current = CurrentTrack.objects.get(pk=1)
except:
except CurrentTrack.DoesNotExist:
current = CurrentTrack(artist="", title="", image="", retries=0)
current.save()
+10 -50
View File
@@ -1,63 +1,23 @@
from datetime import datetime
from django.shortcuts import render
from oliver_screen import utils
from django.http import JsonResponse
from django.views.generic import TemplateView
def index(request):
return render(request, '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})
class IndexView(TemplateView):
template_name = 'public/index.html'
# Mock views
def dummy_get_last_track(request):
played = {'artist': 'The Album Leaf', 'title': 'Another Day (Revised)'}
return render(request, "public/tracks.html", {'played': played})
track = {'artist': 'The Album Leaf', 'title': 'Another Day (Revised)', 'time_since': '10 seconds'}
return JsonResponse(track)
def dummy_now_playing(request):
now_playing = {
track = {
'artist': 'The Album Leaf',
'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})
+3 -2
View File
@@ -17,16 +17,17 @@ TEMPLATE_DEBUG = True
ALLOWED_HOSTS = []
# Application definition
INSTALLED_APPS = (
'flat',
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'rest_framework',
)
INSTALLED_APPS += (
+6 -6
View File
@@ -1,9 +1,9 @@
from django.conf.urls import patterns, include, url
from django.conf.urls import include, url
from django.contrib import admin
urlpatterns = patterns(
'',
url(r'^$', 'oliver_screen.views.index', name='index'),
url(r'', include('oliver_screen.urls')),
from oliver_screen import urls as oliver_screen_urls
urlpatterns = [
url(r'', include(oliver_screen_urls)),
url(r'^admin/', include(admin.site.urls)),
)
]
+2 -1
View File
@@ -8,7 +8,8 @@ https://docs.djangoproject.com/en/1.7/howto/deployment/wsgi/
"""
import os
from django.core.wsgi import get_wsgi_application
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "oliversite.settings")
from django.core.wsgi import get_wsgi_application
application = get_wsgi_application()
+3
View File
@@ -1,3 +1,6 @@
Django>=1.8,<1.9
djangorestframework==3.3.3
django-flat-theme==1.1.3
pylast==1.4.2
mysqlclient==1.3.6
python-dateutil==2.5.2