pig-media-server 0.3.2 → 2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (58) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/bin/pig-media-server +4 -1
  4. data/gulpfile.js +33 -0
  5. data/javascripts/application.js +117 -0
  6. data/javascripts/chromecast.js +57 -0
  7. data/javascripts/components/head.js +106 -0
  8. data/javascripts/components/list.js +104 -0
  9. data/javascripts/components/new_flag.js +23 -0
  10. data/javascripts/components/player.js +171 -0
  11. data/javascripts/components/query_list.js +68 -0
  12. data/javascripts/components/sort.js +64 -0
  13. data/javascripts/components/watch.js +48 -0
  14. data/javascripts/controller.js +62 -0
  15. data/javascripts/custom_list.js +14 -0
  16. data/javascripts/event_dispatcher.js +21 -0
  17. data/javascripts/recent.js +34 -0
  18. data/javascripts/utils.js +22 -0
  19. data/javascripts/video.js +10 -0
  20. data/lib/pig-media-server/api.rb +96 -0
  21. data/lib/pig-media-server/aspect.rb +10 -5
  22. data/lib/pig-media-server/backup.rb +69 -0
  23. data/lib/pig-media-server/cli.rb +60 -25
  24. data/lib/pig-media-server/crawl.rb +41 -5
  25. data/lib/pig-media-server/kindle_send.rb +31 -24
  26. data/lib/pig-media-server/model/data.rb +106 -0
  27. data/lib/pig-media-server/model/migrate.rb +34 -0
  28. data/lib/pig-media-server/model/pig.rb +11 -2
  29. data/lib/pig-media-server/version.rb +1 -1
  30. data/lib/pig-media-server/views/_custom_links.haml +4 -0
  31. data/lib/pig-media-server/views/_ft_flv.haml +1 -0
  32. data/lib/pig-media-server/views/_ft_pdf.haml +0 -0
  33. data/lib/pig-media-server/views/_ft_video.haml +9 -4
  34. data/lib/pig-media-server/views/_link.haml +4 -1
  35. data/lib/pig-media-server/views/_new_flag.haml +1 -1
  36. data/lib/pig-media-server/views/app.scss +10 -1
  37. data/lib/pig-media-server/views/bundle.js +13 -0
  38. data/lib/pig-media-server/views/chromecast.coffee +54 -0
  39. data/lib/pig-media-server/views/config.coffee +13 -1
  40. data/lib/pig-media-server/views/config.haml +27 -4
  41. data/lib/pig-media-server/views/feed.builder +3 -2
  42. data/lib/pig-media-server/views/flv.coffee +33 -0
  43. data/lib/pig-media-server/views/index.haml +11 -5
  44. data/lib/pig-media-server/views/meta.haml +4 -4
  45. data/lib/pig-media-server/views/movie.coffee +65 -39
  46. data/lib/pig-media-server/views/react.haml +22 -0
  47. data/lib/pig-media-server/views/remote.haml +2 -2
  48. data/lib/pig-media-server/views/session.coffee +5 -0
  49. data/lib/pig-media-server/views/storage.coffee +5 -16
  50. data/lib/pig-media-server/views/sub.haml +5 -6
  51. data/lib/pig-media-server/views/subview.coffee +109 -19
  52. data/lib/pig-media-server/views/tv.coffee +97 -0
  53. data/lib/pig-media-server/views/tv.haml +33 -0
  54. data/lib/pig-media-server/views/unread.coffee +10 -0
  55. data/lib/pig-media-server/web.rb +102 -37
  56. data/package.json +23 -0
  57. data/pig-media-server.gemspec +5 -0
  58. metadata +123 -38
@@ -4,28 +4,109 @@ mode = 'eroge'
4
4
  current = null
5
5
 
6
6
  eroge_next = ->
7
- if current == null or current == undefined
8
- current = ps[0]
7
+ unless $('video').get(0)
8
+ if current == null or current == undefined
9
+ current = ps[0]
10
+ else
11
+ current = ps[ids.indexOf($(current).attr('id'))+1]
12
+
13
+ before = ps[ids.indexOf($(current).attr('id'))-1]
14
+ if $(before).find('img').length == 0
15
+ $(before).css display: 'none'
16
+ if $(current).find('img').length != 0
17
+ $('html, body').animate {scrollTop: $(current).offset().top}, 'fast'
18
+ current = ps[ids.indexOf($(current).attr('id'))+1]
9
19
  else
10
- current = ps[ids.indexOf($(current).attr('id'))+1]
11
-
12
- before = ps[ids.indexOf($(current).attr('id'))-1]
13
- if $(before).find('img').length == 0
14
- $(before).css display: 'none'
15
- if $(current).find('img').length != 0
16
- $('html, body').animate {scrollTop: $(current).offset().top}, 'fast'
17
- current = ps[ids.indexOf($(current).attr('id'))+1]
20
+ seek(15)
21
+
18
22
 
19
23
  eroge_prev = ->
20
- if current == null or current ==undefined
21
- current = ps[0]
24
+ unless $('video').get(0)
25
+ if current == null or current ==undefined
26
+ current = ps[0]
27
+ else
28
+ current = ps[ids.indexOf($(current).attr('id'))-1]
29
+ if $(current).find('img').length != 0
30
+ $('html, body').animate {scrollTop: $(current).offset().top}, 'fast'
31
+ $(current).css display: 'block'
22
32
  else
23
- current = ps[ids.indexOf($(current).attr('id'))-1]
24
- if $(current).find('img').length != 0
25
- $('html, body').animate {scrollTop: $(current).offset().top}, 'fast'
26
- $(current).css display: 'block'
33
+ seek(-15)
34
+
35
+ seek = (count)->
36
+ node = document.querySelector 'video'
37
+ node.currentTime = node.currentTime + count
38
+ window.gyazo = ->
39
+ c = document.querySelector '#canvas'
40
+ v = document.querySelector 'video'
41
+ context = c.getContext '2d'
42
+ c.width = v.videoWidth
43
+ c.height = v.videoHeight
44
+ context.drawImage(v, 0, 0)
45
+ url = c.toDataURL()
46
+ $.post('/gyazo', {url: url, point: localStorage.gyazo}).success((data)->
47
+ window.open(data.url, "", "width=500,height=400")
48
+ )
49
+
50
+ window.tweet = ->
51
+ c = document.querySelector '#canvas'
52
+ v = document.querySelector 'video'
53
+ context = c.getContext '2d'
54
+ c.width = v.videoWidth
55
+ c.height = v.videoHeight
56
+ context.drawImage(v, 0, 0)
57
+ url = c.toDataURL()
58
+ $.post('/gyazo/tweet', {url: url}).success((data)->
59
+ true
60
+ )
61
+
62
+ window.tweet_with_comment = ->
63
+ c = document.querySelector '#canvas'
64
+ v = document.querySelector 'video'
65
+ context = c.getContext '2d'
66
+ c.width = v.videoWidth
67
+ c.height = v.videoHeight
68
+ context.drawImage(v, 0, 0)
69
+ url = c.toDataURL()
70
+ comment = prompt 'Tweet'
71
+ $.post('/gyazo/tweet', {url: url, comment: comment}).success((data)->
72
+ true
73
+ )
27
74
 
28
75
 
76
+ video_play = (node)->
77
+ video = $('#url').text()
78
+ $node = $(node)
79
+ console.log "play #{video} #{$node.attr('time')} #{$node.attr('master_id')}"
80
+ $video = $('<video>').attr(
81
+ src: video
82
+ master_id: $node.attr('master_id'),
83
+ image: $node.attr('src'),
84
+ controls: 'controls'
85
+ time: $node.attr('time')
86
+ ).css('max-width': '100%', 'max-height': '100%')
87
+ $("#p_#{$node.attr('master_id')}").html($video)
88
+ $video = $("#p_#{$node.attr('master_id')} video")
89
+ $video.bind('canplay', (e)->
90
+ console.log e
91
+ e.target.currentTime = parseFloat($(e.target).attr('time'))-5
92
+ $(e.target).unbind('canplay')
93
+ e.target.play()
94
+ $('html,body').animate(scrollTop: $(e.target).offset().top)
95
+ )
96
+ $video.get(0).load()
97
+ $video.click(->
98
+ $v = $(this)
99
+ master_id = $v.attr('master_id')
100
+ $("#p_#{master_id}").html(
101
+ $('<img>').attr(
102
+ src: $v.attr('image'),
103
+ class: 'srt_image',
104
+ id: "image_#{master_id}",
105
+ time: $v.attr('time'),
106
+ master_id: master_id
107
+ )
108
+ )
109
+ )
29
110
  window.sub_view = ->
30
111
  for n in $('.srt_line')
31
112
  a = $(n).html().split('<br>')
@@ -34,7 +115,10 @@ window.sub_view = ->
34
115
  $.get('/api/get', name: "sub/caps/#{$('#key').text()}").success (data)->
35
116
  caps = JSON.parse(data)
36
117
  $.each caps, (i,n)->
37
- $("##{i}").before $('<p>').append($('<img>').attr(src: n)).attr(class: 'srt_image', id: "image_#{i}")
118
+ time = parseFloat(i.split('_')[1])/1000
119
+ $("##{i}").before $('<p>').append(
120
+ $('<img>').attr(src: n).attr(class: 'srt_image', id: "image_#{i}", time: time, master_id: i)
121
+ ).attr(id: "p_#{i}")
38
122
  ps = document.querySelectorAll('.srt p')
39
123
  $('h3').after(
40
124
  $('<button>').text('Reset Capture').click ->
@@ -51,7 +135,13 @@ window.sub_view = ->
51
135
  eroge_next()
52
136
  when 75
53
137
  eroge_prev()
138
+ when 71
139
+ gyazo() if document.querySelector('video')
140
+ when 84
141
+ tweet() if document.querySelector('video')
142
+ when 67
143
+ tweet_with_comment() if document.querySelector('video')
144
+
54
145
  $ ->
55
- $(document).on('click', '.srt_image', -> eroge_next()).css('cursor','pointer')
146
+ $(document).on('click', '.srt_image', -> video_play(this)).css('cursor','pointer')
56
147
  #$(document).on('touchend', '.srt_image', -> eroge_next())
57
-
@@ -0,0 +1,97 @@
1
+ TV = ->
2
+ @data = []
3
+ @list = []
4
+ @initial = true
5
+ @get = =>
6
+ $.getJSON('/tv/list').done((data)=>
7
+ @data = data
8
+ @bind()
9
+ @render()
10
+ ).fail((a,b,c)=>
11
+ @bind()
12
+ )
13
+
14
+ @save = =>
15
+ $.post('/tv/list', data: JSON.stringify(@data)).success(=> @render())
16
+
17
+ @cast = =>
18
+ @initial = false
19
+ @list = []
20
+ $.ajaxSetup({ async: false })
21
+ for d in @data
22
+ $.get("/feed", query: d).success((res)=>
23
+ for item in $(res).find('item')
24
+ title = $(item).find('title').text()
25
+ link = $(item).find('link').text()
26
+ date = Date.parse $(item).find('pubDate').text()
27
+ key = $(item).find('key').text()
28
+ @list.push [title, link, date, key]
29
+ )
30
+ @list = @list.sort((a,b)->
31
+ a[2] > b[2]
32
+ )
33
+ for l in @list
34
+ @create_link(l)
35
+ $.each $('a.watch'), -> watch(this)
36
+ set_new()
37
+
38
+ for n in $('.main_span')
39
+ $(n).remove() if n.dataset.new == undefined
40
+
41
+ $('a.watch').first().click()
42
+ @create_link = (ary)=>
43
+ $span = $('<span>')
44
+ $span.addClass('main_span')
45
+ $span.attr(id: ary[3])
46
+
47
+ $new_flag = $('<span>').addClass('new_flag')
48
+ $new_flag.attr(key: "movie/#{ary[3]}", style: 'color:lime; font-size:large')
49
+
50
+ $a = $('<a>')
51
+ $a.addClass('main_link').text(ary[0]).attr(href: ary[1], key: ary[3])
52
+
53
+ $watch = $('<a>')
54
+ $watch.addClass('watch').text(' Watch').attr(href: 'javascript:void(0)', url: ary[1], key: ary[3], title: ary[0])
55
+
56
+ $span.append($new_flag)
57
+ $span.append($a)
58
+ $span.append($watch)
59
+ $('#list').append($span)
60
+
61
+
62
+
63
+ @bind = =>
64
+ $('#start').click => @cast()
65
+ $('button').click =>
66
+ t = $('input#title').val()
67
+ unless t == ''
68
+ @data.push t
69
+ @save()
70
+
71
+ @delete = ($node)=>
72
+ d = $node.attr('data')
73
+ @data.splice @data.indexOf(d), 1
74
+ @save()
75
+
76
+ $node.parent().remove()
77
+
78
+
79
+ @render = =>
80
+ $('ul').html('')
81
+ for d in @data
82
+ $li = $('<li>').text(d+" ")
83
+ $li.append(
84
+ $('<a>').text('削除').attr(href: 'javascript:void(0)', data: d).click((e)=>
85
+ @delete($(e.target))
86
+ )
87
+ )
88
+ $('ul').append $li
89
+ @cast() if @initial
90
+
91
+ @start = =>
92
+ @get()
93
+ @start()
94
+
95
+ $ ->
96
+ if $('#tv').text() == 'true'
97
+ t = new TV()
@@ -0,0 +1,33 @@
1
+ !!!
2
+ %title= title
3
+ %meta(content='width=320, initial-scale=1.0, maximum-scale=1.0, user-scalable=no' name='viewport')
4
+ %script{src: 'https://code.jquery.com/jquery.min.js'}
5
+ -%w{flv storage session movie unread book book2 swipe tv}.each do |js|
6
+ %script{src: "/#{js}.js"}
7
+ %link{href: '/app.css', rel: 'stylesheet', type: 'text/css'}
8
+ :css
9
+ .none{display:none}
10
+ #area=''
11
+ #all
12
+ -unless session[:user_id]
13
+ %a{href: '/sessions'} Login
14
+ -else
15
+ Login as
16
+ %span#user_id=session[:user_id]
17
+ %a{href: 'javascript:void(0)', onclick: 'logout()'} Logout
18
+ %p=flash[:notice]
19
+ %h1
20
+ %a{href: '/'} #{config['page_title']}
21
+ %h2
22
+ %a#start{href: 'javascript:void(0)'} Cast 開始
23
+ %h3 お気に入りの番組(クエリ)を登録してください
24
+ %input#title
25
+ %button 追加
26
+ %ul
27
+ %li=''
28
+ %h3 再生リスト
29
+ %p#list=''
30
+ .none
31
+ %span#action=@action
32
+ %span#tv=true
33
+ %canvas#canvas
@@ -6,6 +6,16 @@ window.set_new = ->
6
6
  unless recents[key]
7
7
  $(this).text 'New!'
8
8
  $(this).parent().attr('data-new': true)
9
+ $(this).click ->
10
+ $node = $(this)
11
+ window.get_recents (recents)->
12
+ recents[$node.attr('key')] = {time: 0, type: 'movie'}
13
+ window.save_recents(recents)
14
+ setTimeout ->
15
+ $('.new_flag').text('')
16
+ $('.main_span').attr('data-new': '')
17
+ window.set_new()
18
+ , 1000
9
19
  count += 1
10
20
  if count == 0
11
21
  $('.main_link').css('margin-left', '0')
@@ -1,4 +1,5 @@
1
1
  require 'pig-media-server'
2
+ require 'pig-media-server/api'
2
3
  require 'pig-media-server/model/pig'
3
4
  require 'pig-media-server/model/comic'
4
5
  require 'sinatra/base'
@@ -12,21 +13,39 @@ require 'coffee_script'
12
13
  require 'rack/csrf'
13
14
  require 'redcarpet'
14
15
  require 'json'
16
+ require 'twitter'
17
+ require 'tempfile'
15
18
 
16
19
  module PigMediaServer
17
20
  CONFIG = Pit.get 'Pig Media Sever'
18
21
  class UserData
19
- def self.save json, user_id, path
20
- open("#{path}/#{user_id}.json", "w"){|f| f.puts json}
21
- true
22
+ def self.save user_id, key, value
23
+ AppData.set("user_data/#{user_id}/#{key}", value)
22
24
  end
23
25
 
24
- def self.load user_id, path
25
- return JSON.parse(open("#{path}/#{user_id}.json").read) rescue return {}
26
+ def self.load user_id, key
27
+ AppData.find("user_data/#{user_id}/#{key}")
26
28
  end
27
29
  end
28
30
 
29
31
  class Gyazo
32
+ def self.tweet url, comment, key, secret, token, token_secret, c
33
+ $config = $config ||Pit.get("Pig Media Server")
34
+ name = $config['gyazo_path'] + "/#{rand(256**16).to_s(16)}.png"
35
+ jpg = name.sub(/png$/, 'jpg')
36
+ img = url.sub(/data:image\/png;base64,/, '').unpack('m').first
37
+ open(name, 'w'){|f| f.puts img}
38
+ system "gm", "convert", name, jpg
39
+ client = Twitter::REST::Client.new do |config|
40
+ config.consumer_key = key
41
+ config.consumer_secret = secret
42
+ config.access_token = token
43
+ config.access_token_secret = token_secret
44
+ end
45
+ client.update_with_media(comment.to_s, open(jpg))# rescue nil
46
+ FileUtils.rm name
47
+ FileUtils.rm jpg
48
+ end
30
49
  def self.post url, point
31
50
  imagedata = url.sub(/data:image\/png;base64,/, '').unpack('m').first
32
51
  boundary = '----BOUNDARYBOUNDARY----'
@@ -60,22 +79,17 @@ EOF
60
79
 
61
80
  class Web < Sinatra::Base
62
81
  register Sinatra::Flash
82
+ include PigMediaServer::API
63
83
 
64
84
  configure do
65
- set :sessions, true
66
85
  set :haml, escape_html: true
67
86
  set :haml, attr_wrapper: '"'
68
- use Rack::Session::Cookie, expire_after: 60*60*24*28, secret: $session_secret || rand(256**16).to_s(16)
87
+ set :logging, true
88
+ use Rack::Session::Cookie, key: 'pigmeidaserver', secret: $session_secret || rand(256**16).to_s(16)
69
89
  end
70
90
 
71
91
  get '/' do
72
- if params[:query]
73
- redirect '/latest' if params[:query].empty?
74
- @page = params[:page].to_i < 1 ? 1 : params[:page].to_i
75
- @action = 'list'
76
- @list = Pig.search params.merge(page: @page)
77
- end
78
- haml :index
92
+ haml :react
79
93
  end
80
94
 
81
95
  get '/feed' do
@@ -85,23 +99,21 @@ EOF
85
99
  end
86
100
 
87
101
  get '/custom' do
88
- c = config['custom_list'][params[:name]]
89
- @page = params[:page].to_i < 1 ? 1 : params[:page].to_i
90
- @action = 'list'
91
- @list = Pig.find JSON.parse(open(c).read)
92
- @no_paging = true
93
- haml :index
102
+ haml :react
94
103
  end
95
104
 
96
105
  get '/latest' do
97
- @page = params[:page].to_i < 1 ? 1 : params[:page].to_i
98
- size = params[:size] ? params[:size].to_i : 50
99
- @list = Groonga['Files'].select.paginate([key: 'mtime', order: 'descending'], size: size, page: @page).map{|x| Pig.new(x)}
100
- @action = 'list'
101
- haml :index
106
+ haml :react
102
107
  end
103
108
 
104
- get('/meta/:key'){@p = Pig.find(params[:key]);haml :meta}
109
+ get '/recommend' do
110
+ haml :react
111
+ end
112
+
113
+ #get('/meta/:key'){@p = Pig.find(params[:key]);haml :meta}
114
+ get('/meta/:key') do
115
+ haml :react
116
+ end
105
117
  get('/sub/:key'){@p = Pig.find(params[:key]);haml :sub}
106
118
  get('/webvtt/:key'){@p = Pig.find(params[:key]); content_type :text; @p.webvtt}
107
119
  get '/delete/:key' do
@@ -140,6 +152,15 @@ EOF
140
152
  content_type :json
141
153
  {url: url}.to_json
142
154
  end
155
+ post '/gyazo/tweet' do
156
+ consumer_key = UserData.load session[:user_id], 'consumer_key'
157
+ consumer_secret = UserData.load session[:user_id], 'consumer_secret'
158
+ token = UserData.load session[:user_id], 'token'
159
+ token_secret = UserData.load session[:user_id], 'token_secret'
160
+ PigMediaServer::Gyazo.tweet params[:url], params[:comment], consumer_key, consumer_secret, token, token_secret, config()
161
+ true
162
+ end
163
+
143
164
 
144
165
  get '/remote' do
145
166
  if request.xhr? and params[:key]
@@ -171,18 +192,41 @@ EOF
171
192
 
172
193
  post '/sessions' do
173
194
  session[:user_id] = params[:user_id]
195
+ p session
174
196
  redirect '/'
175
197
  end
176
198
 
177
- get('/hash'){content_type :json; hash().to_json}
178
- post('/hash'){PigMediaServer::UserData.save params[:json], session[:user_id], config['user_data_path']}
199
+ get '/tv' do
200
+ @action = 'list'
201
+ haml :tv
202
+ end
203
+
204
+ get '/tv/list' do
205
+ content_type :json
206
+ UserData.load(session[:user_id], 'tv-list').to_json
207
+ end
208
+
209
+ post '/tv/list' do
210
+ UserData.save(session[:user_id], 'tv-list', JSON.parse(params[:data]))
211
+ end
212
+
213
+ get('/data'){UserData.load(session[:user_id], params[:key])}
214
+ post('/data'){UserData.save(session[:user_id], params[:key], params[:value])}
215
+ get('/recents'){content_type :json; p session[:user_id];Recents.list(session[:user_id]).to_json}
216
+ post '/recents' do
217
+ JSON.parse(params[:data]).each{|k,v|
218
+ Recents.recent session[:user_id], k
219
+ }
220
+ 'true'
221
+ end
179
222
 
180
223
  get('/config'){haml :config}
181
224
  get('/book2.js'){content_type :js;erb :book2}
182
225
  get('/swipe.js'){content_type :js;erb :swipe}
183
226
  get('/*.css'){scss params[:splat].first.to_sym}
227
+ get('/bundle.js'){content_type :js; open(File::dirname(__FILE__)+"/views/bundle.js").read}
184
228
  get('/*.js'){coffee params[:splat].first.to_sym}
185
-
229
+
186
230
  post '/api/save' do
187
231
  key = Digest::MD5.hexdigest(params[:name]).to_s
188
232
  FileUtils.mkdir_p "#{config['user_data_path']}/api_data"
@@ -201,17 +245,34 @@ EOF
201
245
  post '/api/capapi' do
202
246
  record = Groonga['Files'][params[:key]]
203
247
  name = "#{rand(256**16).to_s(16)}.jpg"
204
- system "ffmpeg -ss #{params[:time]} -vframes 1 -i \"#{record.path}\" -f image2 #{config['gyazo_path']}/#{name}"
248
+ system "avconv -ss #{params[:time]} -i \"#{record.path}\" -f image2 -s 1280x720 -vframes 1 #{config['gyazo_path']}/#{name}"
249
+ #system "gm convert -resize 1280x720! #{config['gyazo_path']}/#{name} #{config['gyazo_path']}/#{name}"
205
250
  "#{config['gyazo_prefix']}/#{name}"
206
251
  end
207
252
 
253
+ get '/star' do
254
+ Stars.star(session[:user_id], params[:key])
255
+ redirect params[:href]
256
+ end
257
+
258
+ get '/stars' do
259
+ @page = params[:page].to_i < 1 ? 1 : params[:page].to_i
260
+ @action = 'list'
261
+ @list = Stars.list session[:user_id]
262
+ haml :index
263
+ end
264
+
208
265
 
209
266
  helpers do
210
267
  def config
211
268
  $config = Pit.get("Pig Media Server") unless $config
269
+ $config['page_title'] = 'Pig Media Server' unless $config['page_title']
212
270
  $config
213
271
  end
214
272
  def h str; CGI.escapeHTML str.to_s; end
273
+ def remote?
274
+ UserData.load(session[:user_id], 'remote')
275
+ end
215
276
  def partial(template, *args)
216
277
  options = args.last.is_a?(Hash) ? args.pop : {}
217
278
  options.merge!(:layout => false)
@@ -223,12 +284,9 @@ EOF
223
284
  haml(template, options)
224
285
  end
225
286
  end
226
- def hash
227
- if session[:user_id]
228
- return PigMediaServer::UserData.load session[:user_id], config['user_data_path']
229
- else
230
- return {}
231
- end
287
+
288
+ def star? key
289
+ Stars.star?(session[:user_id], key)
232
290
  end
233
291
 
234
292
  def markdown str
@@ -257,8 +315,15 @@ EOF
257
315
  "%.#{precision}f TB" % (number / 1099511627776)
258
316
  end.sub(/([0-9]\.\d*?)0+ /, '\1 ' ).sub(/\. /,' ').encode('UTF-8')
259
317
  rescue
260
- nil
318
+ ""
261
319
  end
320
+
321
+ def title
322
+ base = $config['page_title']
323
+ return "#{params[:query]} - #{base}" if params[:query]
324
+ return "#{@p.name} - #{base}" if @p
325
+ base
326
+ end
262
327
  end
263
328
  end
264
329
 
data/package.json ADDED
@@ -0,0 +1,23 @@
1
+ {
2
+ "main": "javascript/main.js",
3
+ "description": "music",
4
+ "dependencies": {
5
+ "react": "",
6
+ "react-dom": "",
7
+ "moment": "",
8
+ "moment-timezone": "",
9
+ "jquery": ""
10
+ },
11
+ "devDependencies": {
12
+ "browser-sync": "",
13
+ "browserify": "",
14
+ "babelify": "",
15
+ "gulp-webserver": "",
16
+ "gulp": "3.9.0",
17
+ "gulp-babel": "",
18
+ "vinyl-source-stream": "",
19
+ "vinyl-buffer": "",
20
+ "babel": "",
21
+ "gulp-uglify": ""
22
+ }
23
+ }
@@ -36,4 +36,9 @@ Gem::Specification.new do |gem|
36
36
  gem.add_dependency 'builder'
37
37
  gem.add_dependency 'coffee-script'
38
38
  gem.add_dependency 'sinatra-flash'
39
+ gem.add_dependency 'twitter'
40
+ gem.add_dependency 'thor'
41
+ gem.add_dependency 'mechanize'
42
+ gem.add_dependency 'activesupport'
43
+
39
44
  end