From 8f85f1a74cb9f8796638a00c5b78541d027b9845 Mon Sep 17 00:00:00 2001 From: raylu Date: Sat, 6 Aug 2011 19:55:57 -0700 Subject: other users' adds will show up; temp crappy internet fixes --- Makefile | 4 +- main.go | 129 +++++++++++++++++++++++++++------------ static/base.css | 7 ++- static/jquery.hive.pollen.min.js | 3 + static/poller.js | 33 ++++++++++ static/script.js | 87 +++++++++++++++----------- templates/p.html | 42 ++++--------- updates.go | 51 ++++++++++++++++ 8 files changed, 250 insertions(+), 106 deletions(-) create mode 100644 static/jquery.hive.pollen.min.js create mode 100644 static/poller.js create mode 100644 updates.go diff --git a/Makefile b/Makefile index 47cbb29..b3a5966 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ all: aa -main.6: main.go - 6g main.go +main.6: main.go updates.go + 6g main.go updates.go aa: main.6 6l -o aa main.6 diff --git a/main.go b/main.go index c139b99..965e000 100644 --- a/main.go +++ b/main.go @@ -6,6 +6,9 @@ import ( "exp/template" mysql "github.com/Philio/GoMySQL" "os" + "json" + "strconv" + "time" ) type Song struct { @@ -15,11 +18,10 @@ type Song struct { } type Playlist struct { Id string - Songs []*Song } var templates map[string]*template.Template -const debug = false +const debug = true var db *mysql.Client // given an id ('abcd1234'), return the pid (1) @@ -59,41 +61,7 @@ func playlist(w http.ResponseWriter, r *http.Request) { http.Redirect(w, r, "/", 303) return } - query, err := db.Prepare("SELECT `yid`,`title`,`user` FROM `playlist` JOIN `song` WHERE `id` = ?;") - if err != nil { - http.Error(w, err.String(), http.StatusInternalServerError) - return - } - err = query.BindParams(id) - if err != nil { - http.Error(w, err.String(), http.StatusInternalServerError) - return - } - err = query.Execute() - if err != nil { - http.Error(w, err.String(), http.StatusInternalServerError) - return - } - - playlist := Playlist{Id: id, Songs: make([]*Song, 0, 2)} - for { - song := new(Song) - query.BindResult(&song.Yid, &song.Title, &song.User) - eof, err := query.Fetch() - if err != nil { - http.Error(w, err.String(), http.StatusInternalServerError) - return - } - if eof { - break - } - playlist.Songs = append(playlist.Songs, song) - } - err = query.FreeResult() - if err != nil { - http.Error(w, err.String(), http.StatusInternalServerError) - return - } + playlist := Playlist{Id: id} if debug { t, err := template.ParseFile("templates/p.html") @@ -108,7 +76,7 @@ func playlist(w http.ResponseWriter, r *http.Request) { return } } else { - err = templates["p"].Execute(w, playlist) + err := templates["p"].Execute(w, playlist) if err != nil { fmt.Fprintln(os.Stderr, err.String()) } @@ -133,7 +101,10 @@ func add(w http.ResponseWriter, r *http.Request) { http.Error(w, err.String(), http.StatusInternalServerError) return } + w.Write([]byte("1")) + addUpdate(pid, addAction, + &Song{Yid: q.Get("yid"), Title: q.Get("title"), User: q.Get("user")}) } func remove(w http.ResponseWriter, r *http.Request) { @@ -151,7 +122,85 @@ func remove(w http.ResponseWriter, r *http.Request) { http.Error(w, err.String(), http.StatusInternalServerError) return } + w.Write([]byte("1")) + addUpdate(pid, removeAction, &Song{Yid: q.Get("yid")}) +} + +func poll(w http.ResponseWriter, r *http.Request) { + q := r.URL.Query() + timestamp := q.Get("timestamp") + if timestamp == "0" { + query, err := db.Prepare("SELECT `yid`,`title`,`user` FROM `playlist` JOIN `song` WHERE `id` = ?;") + if err != nil { + http.Error(w, err.String(), http.StatusInternalServerError) + return + } + err = query.BindParams(q.Get("pid")) + if err != nil { + http.Error(w, err.String(), http.StatusInternalServerError) + return + } + err = query.Execute() + if err != nil { + http.Error(w, err.String(), http.StatusInternalServerError) + return + } + + updates := make([]Update, 0, 2) + for { + song := new(Song) + query.BindResult(&song.Yid, &song.Title, &song.User) + eof, err := query.Fetch() + if err != nil { + http.Error(w, err.String(), http.StatusInternalServerError) + return + } + if eof { + break + } + updates = append(updates, Update{Song: song, Action: addAction, Timestamp: time.Nanoseconds()}) + } + err = query.FreeResult() + if err != nil { + http.Error(w, err.String(), http.StatusInternalServerError) + return + } + + output, err := json.MarshalForHTML(updates) + if err != nil { + http.Error(w, err.String(), http.StatusInternalServerError) + return + } + w.Write(output) + } else { + timestamp, err := strconv.Atoi64(q.Get("timestamp")) + if err != nil { + http.Error(w, err.String(), http.StatusInternalServerError) + return + } + var update *Update + for i := 0; i < 30; i++ { + update = getUpdates(getpid(q.Get("pid")), timestamp) + if update != nil { + w.Write([]byte("[")) + for update != nil { + output, err := json.MarshalForHTML(update) + if err == nil { + w.Write(output) + } + update = update.Next + if update != nil { + w.Write([]byte(",")) + } + } + w.Write([]byte("]")) + return + } + time.Sleep(1e9) // 1 second + } + w.Write([]byte("[]")) + } } func main() { @@ -166,7 +215,8 @@ func main() { } var err os.Error - db, err = mysql.DialTCP("raylu.net", "audio", "audio", "audio") + //db, err = mysql.DialTCP("raylu.net", "audio", "audio", "audio") + db, err = mysql.DialTCP("173.228.31.111", "audio", "audio", "audio") if err != nil { fmt.Println(err) os.Exit(1) @@ -176,6 +226,7 @@ func main() { http.HandleFunc("/p/", playlist) http.HandleFunc("/add/", add) http.HandleFunc("/remove/", remove) + http.HandleFunc("/poll/", poll) err = http.ListenAndServe("localhost:8000", nil) if err != nil { fmt.Println(err) diff --git a/static/base.css b/static/base.css index c932d6f..00ad6f1 100644 --- a/static/base.css +++ b/static/base.css @@ -63,6 +63,7 @@ header label[for="user"] { } header input#user { left: 570px; + top: 0px; } header label[for="song"] { left: 300px; @@ -137,7 +138,7 @@ section.song div.user { } section.song div.remove { position: absolute; - top: 0px; + top: 0; right: 5px; font-weight: bold; cursor: pointer; @@ -145,8 +146,12 @@ section.song div.remove { section.song div.thumb { float: left; width: 150px; + height: 90px; overflow: hidden; } +section.song div.thumb img { + height: 90px; +} section.song canvas, section.song > img { position: relative; top: 20px; diff --git a/static/jquery.hive.pollen.min.js b/static/jquery.hive.pollen.min.js new file mode 100644 index 0000000..e2557b3 --- /dev/null +++ b/static/jquery.hive.pollen.min.js @@ -0,0 +1,3 @@ +// https://github.com/rwldrn/jquery-hive +// http://dev.pollenjs.com +if(!Array.prototype.forEach){Array.prototype.forEach=function(a){var b=this.length||0,c=0,d=arguments[1];if(typeof a!=="function"){throw new TypeError}for(;c=0||(new RegExp(b,"ig")).test(a)){return true}return false},_regExpEscape:function(a){if((new RegExp(/([[\]\/\\])/g)).test(a)===false){return String(a).replace(/([.*+?^=!:${}()|[\]\/\\])/g,"\\$1")}return a}},array:{each:function(a,b){var c=0,d=a.length;if(Pollen.evaluate.isArr(a)){for(;cd?1:0}).pick("value")},compact:function(a){var b=[undefined,null,false],c=Pollen.evaluate.isArr(a)?[]:{},d=function(a){var b=a.length,e="";for(var f in a){e=a[f];if(Pollen.evaluate.isArr(e)){d(e)}else{if(!Pollen.evaluate.eq(e,[undefined,null,false])){if(Pollen.evaluate.isArr(c)){c[c.length]=e}else{c[e]=e}}}}return true};d(a);return c}},object:{keys:function(a){var b=[];for(var c in a){if(a[c]){b[b.length]=c}}return b},values:function(a){var b=[];for(var c in a){if(a[c]){b[b.length]=a[c]}}return b},extend:function(a,b){if(Pollen.evaluate.isNull(a)&&Pollen.evaluate.isDef(b)){return b}for(var c in b){if(hasOwn.call(b,c)){var d=b[c],e=a[c];if(!Pollen.evaluate.isNull(d)){a[c]=e&&typeof d=="object"&&typeof e=="object"?Pollen.array.merge(e,d):d}}}return a}},ajax:{_options:{url:"",data:"",dataType:"",success:Pollen.noop,type:"GET",sync:navigator.userAgent.toLowerCase().indexOf("safari/")!=-1?false:true,xhr:function(){return new XMLHttpRequest}},get:function(a){a.type="GET";this._ajax(a)},post:function(a){a.type="POST";this._ajax(a)},_ajax:function(a){var b=Pollen.object.extend(this._options,a),c=b.dataType=="json"?true:false,d=b.type.toLowerCase(),e=b.xhr(),f=b.success;if(!Pollen.evaluate.isStr(b.data)){b.data=Pollen.data.param(b.data)}if(d=="get"&&b.data.length){b.url+=((new RegExp(/\?/)).test(b.url)?"&":"?")+b.data}if(e){Pollen.ajax._confXHR(e,b,c,f);e.open(b.type,b.url,b.sync);e.setRequestHeader("X-Requested-With","Worker-XMLHttpRequest");e.setRequestHeader("X-Worker-Hive","Pollen-JS");if(Pollen.evaluate.isDef(Pollen.identity)){e.setRequestHeader("X-Pollen-Thread-Id",Pollen.identity)}if(d=="post"){e.setRequestHeader("Content-Type","application/x-www-form-urlencoded")}e.send(d=="post"?b.data:null)}},_confXHR:function(a,b,c,d){function h(){if(d){d.call(a,e)}}var e,f;var g=a.onreadystatechange=function(){if(a.readyState==4){f=Pollen.evaluate.isJson(a.responseText)?JSON.parse(a.responseText):null;e=c?f:{text:a.responseText,xml:a.responseXML,json:f};h()}};return a}},data:{param:function(a){function c(a,c){c=Pollen.evaluate.isFn(c)?c():c;b[b.length]=encodeURIComponent(a)+"="+encodeURIComponent(c)}var b=[];Pollen.array.each(a,function(a,b){c(a,b)});if(arguments.length==2&&arguments[1]===true){c("WORKER_ID",Pollen.identity)}return b.join("&").replace(/%20/g,"+")},cache:{pollen:+(new Date)},storage:function(a,b){if(a&&b===undefined){return Pollen.data.cache[a]?Pollen.data.cache[a]:false}Pollen.data.cache[a]=b;return Pollen.data.cache[a]},query:function(){function distinctFilter(a,b){var c=[],d={},e=a.length,f=0;for(;f0?"`]":a.charAt(0)=='"'||a.charAt(0)=="'"?"`"+(strs.push(a)-1):a}).replace(/([^<>=]=)([^=])/g,"$1=$2").replace(/@|(\.\s*)?[a-zA-Z\$_]+(\s*:)?/g,function(a){return a.charAt(0)=="."?a:a=="@"?"$obj":(a.match(/:|^(\$|Math|true|false|null)$/)?"":"$obj.")+a}).replace(/\.?\.?\[(`\]|[^\]])*\]|\?.*|\.\.([\w\$_]+)|\.\*/g,function(a,b,c){var d=a.match(/^\.?\.?(\[\s*\^?\?|\^?\?|\[\s*==)(.*?)\]?$/);if(d){var e="";if(a.match(/^\./)){pcall("expand");e=",true)"}pcall(d[1].match(/\=/)?"Pollen.array.map":d[1].match(/\^/)?"distinctFilter":"Pollen.array.filter");return e+",function($obj){return "+d[2]+"})"}d=a.match(/^\[\s*([\/\\].*)\]/);if(d){return".concat().sort(function(a,b){"+d[1].replace(/\s*,?\s*([\/\\])\s*([^,\\\/]+)/g,function(a,b,c){return"var av= "+c.replace(/\$obj/,"a")+",bv= "+c.replace(/\$obj/,"b")+";if(av>bv||bv==null){return "+(b=="/"?1:-1)+";}\n"+"if(bv>av||av==null){return "+(b=="/"?-1:1)+";}\n"})+"})"}d=a.match(/^\[(-?[0-9]*):(-?[0-9]*):?(-?[0-9]*)\]/);if(d){pcall("slice");return","+(d[1]||0)+","+(d[2]||0)+","+(d[3]||1)+")"}if(a.match(/^\.\.|\.\*|\[\s*\*\s*\]|,/)){pcall("expand");return(a.charAt(1)=="."?",'"+c+"'":a.match(/,/)?","+a:"")+")"}return a}).replace(/(\$obj\s*(\.\s*[\w_$]+\s*)*)(==|~)\s*`([0-9]+)/g,makeRegex).replace(/`([0-9]+)\s*(==|~)\s*(\$obj(\s*\.\s*[\w_$]+)*)/g,function(a,b,c,d,e){return makeRegex(a,d,e,c,b)});query=prefix+(query.charAt(0)=="$"?"":"$")+query.replace(/`([0-9]+|\])/g,function(a,b){return b=="]"?"]":strs[b]});executor=eval("1&&function($,$1,$2,$3,$4,$5,$6,$7,$8,$9){var $obj=$;return "+query+"}");for(var i=0;i\ - \ -
' + user + '
\ -
×
\ -
\ -
\ - ' + id + '\ -
\ - \ -
\ - Play\ - Pause\ - Stop\ -
\ - \ - '); - ids.push(id); - drawBar(id); + drawAdd(params); } ); } +function drawAdd(s) { + $('article').append('\ +
\ + \ +
' + s.user + '
\ +
×
\ +
\ +
\ + ' + s.yid + '\ +
\ + \ +
\ + Play\ + Pause\ + Stop\ +
\ +
\ + '); + ids.push(s.yid); + drawBar(s.yid); +} + function remove(id) { var user = $('#user').val(); params = { @@ -182,10 +199,10 @@ function remove(id) { 'yid': id, } $.getJSON('/remove/', params, - function(data) { + function (data) { if (data == 1) { var element = $('#' + id); - element.slideUp(100, function() { + element.slideUp(100, function () { element.remove(); }); } diff --git a/templates/p.html b/templates/p.html index 8f7557f..f63c673 100644 --- a/templates/p.html +++ b/templates/p.html @@ -3,7 +3,10 @@ Audio Axis + + @@ -22,43 +25,24 @@
- {{range .Songs}} -
- -
- {{.User}} -
-
×
-
-
- {{.Yid}} -
- -
- Play - Pause - Stop -
-
- {{else}} - No songs found - {{end}}
- {{$first := index .Songs 0}} + {{$first := "82KEVKGRAhI"}} + + + diff --git a/updates.go b/updates.go new file mode 100644 index 0000000..c36bda8 --- /dev/null +++ b/updates.go @@ -0,0 +1,51 @@ +package main + +import ( + "time" +) + +const ( + addAction = 0 + removeAction = 1 +) +type Update struct { + Song *Song + Action uint8 + Timestamp int64 + Next *Update +} +var headUpdates map[int]*Update +var tailUpdates map[int]*Update + +func init() { + headUpdates = make(map[int]*Update) + tailUpdates = make(map[int]*Update) +} + +func addUpdate(pid int, action uint8, song *Song) { + update := new(Update) + update.Song = song + update.Action = action + update.Timestamp = time.Nanoseconds() + pup, ok := tailUpdates[pid] + if ok { + pup.Next = update + } else { + headUpdates[pid] = update + } + tailUpdates[pid] = update +} + +func getUpdates(pid int, timestamp int64) *Update { + pup, ok := headUpdates[pid] + if !ok { + return nil + } + for pup != nil { + if pup.Timestamp > timestamp { + return pup + } + pup = pup.Next + } + return nil +} -- cgit v1.2.3