infod 0.0.2 → 0.0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/infod.rb +51 -12
- data/infod/{Th/404.rb → 404.rb} +4 -16
- data/infod/500.rb +53 -0
- data/infod/GET.rb +104 -0
- data/infod/HEAD.rb +23 -0
- data/infod/HTTP.rb +105 -0
- data/infod/{Th/PATCH.rb → PATCH.rb} +0 -0
- data/infod/POST.rb +34 -0
- data/infod/audio.rb +30 -0
- data/infod/blog.rb +34 -0
- data/infod/cal.rb +72 -0
- data/infod/{Es/code.rb → code.rb} +7 -4
- data/infod/constants.rb +55 -0
- data/infod/{Es/css.rb → css.rb} +0 -0
- data/infod/{Es/csv.rb → csv.rb} +0 -0
- data/infod/{Es/du.rb → du.rb} +0 -0
- data/infod/edit.rb +73 -0
- data/infod/{H/facets.rb → facets.rb} +20 -11
- data/infod/{Es/feed.rb → feed.rb} +17 -16
- data/infod/{Es/find.rb → find.rb} +2 -3
- data/infod/forum.rb +13 -0
- data/infod/{Es/fs.rb → fs.rb} +5 -2
- data/infod/glob.rb +26 -0
- data/infod/graph.rb +131 -0
- data/infod/{Es/grep.rb → grep.rb} +3 -3
- data/infod/{Es/groonga.rb → groonga.rb} +35 -26
- data/infod/{H/histogram.rb → histogram.rb} +23 -16
- data/infod/html.rb +231 -0
- data/infod/{Es/image.rb → image.rb} +16 -26
- data/infod/{Es/index.rb → index.rb} +44 -49
- data/infod/infod.rb +51 -12
- data/infod/json.rb +38 -0
- data/infod/{Es/kv.rb → kv.rb} +3 -9
- data/infod/{Y.rb → lambda.rb} +18 -1
- data/infod/ls.rb +49 -0
- data/infod/mail.rb +108 -0
- data/infod/{Es/man.rb → man.rb} +3 -15
- data/infod/{H/microblog.rb → microblog.rb} +22 -31
- data/infod/{K.rb → mime.rb} +68 -52
- data/infod/{N.rb → names.rb} +77 -56
- data/infod/page.rb +13 -0
- data/infod/postscript.rb +26 -0
- data/infod/rdf.rb +51 -0
- data/infod/{Rb.rb → ruby.rb} +18 -33
- data/infod/{Es/schema.rb → schema.rb} +23 -8
- data/infod/{Es/search.rb → search.rb} +5 -11
- data/infod/{Es/sh.rb → sh.rb} +0 -0
- data/infod/{Es/text.rb → text.rb} +33 -29
- data/infod/{H/threads.rb → threads.rb} +20 -27
- data/infod/{H/time.rb → time.rb} +14 -34
- data/infod/{H/wiki.rb → wiki.rb} +0 -0
- metadata +53 -64
- data/config.ru +0 -3
- data/infod/Es.rb +0 -31
- data/infod/Es/filter.rb +0 -75
- data/infod/Es/glob.rb +0 -22
- data/infod/Es/html.rb +0 -271
- data/infod/Es/in.rb +0 -68
- data/infod/Es/json.rb +0 -68
- data/infod/Es/ls.rb +0 -58
- data/infod/Es/mail.rb +0 -87
- data/infod/Es/mime.rb +0 -59
- data/infod/Es/out.rb +0 -52
- data/infod/Es/pager.rb +0 -34
- data/infod/Es/pdf.rb +0 -19
- data/infod/Es/rdf.rb +0 -35
- data/infod/H.rb +0 -15
- data/infod/H/audio.rb +0 -19
- data/infod/H/blog.rb +0 -15
- data/infod/H/cal.rb +0 -81
- data/infod/H/edit.rb +0 -88
- data/infod/H/forum.rb +0 -4
- data/infod/H/hf.rb +0 -114
- data/infod/H/mail.rb +0 -92
- data/infod/H/who.rb +0 -30
- data/infod/Th.rb +0 -36
- data/infod/Th/500.rb +0 -41
- data/infod/Th/GET.rb +0 -62
- data/infod/Th/HEAD.rb +0 -5
- data/infod/Th/POST.rb +0 -39
- data/infod/Th/perf.rb +0 -37
- data/infod/Th/uid.rb +0 -24
- data/infod/Th/util.rb +0 -89
data/infod.rb
CHANGED
@@ -1,12 +1,51 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
1
|
+
%w{
|
2
|
+
constants
|
3
|
+
lambda
|
4
|
+
mime
|
5
|
+
404
|
6
|
+
500
|
7
|
+
audio
|
8
|
+
blog
|
9
|
+
cal
|
10
|
+
code
|
11
|
+
css
|
12
|
+
csv
|
13
|
+
du
|
14
|
+
edit
|
15
|
+
facets
|
16
|
+
feed
|
17
|
+
find
|
18
|
+
forum
|
19
|
+
fs
|
20
|
+
GET
|
21
|
+
glob
|
22
|
+
graph
|
23
|
+
grep
|
24
|
+
groonga
|
25
|
+
HEAD
|
26
|
+
histogram
|
27
|
+
html
|
28
|
+
HTTP
|
29
|
+
image
|
30
|
+
index
|
31
|
+
json
|
32
|
+
kv
|
33
|
+
ls
|
34
|
+
mail
|
35
|
+
man
|
36
|
+
microblog
|
37
|
+
names
|
38
|
+
page
|
39
|
+
PATCH
|
40
|
+
POST
|
41
|
+
postscript
|
42
|
+
rdf
|
43
|
+
ruby
|
44
|
+
schema
|
45
|
+
search
|
46
|
+
sh
|
47
|
+
text
|
48
|
+
threads
|
49
|
+
time
|
50
|
+
wiki
|
51
|
+
}.map{|e|require_relative e}
|
data/infod/{Th/404.rb → 404.rb}
RENAMED
@@ -1,4 +1,3 @@
|
|
1
|
-
watch __FILE__
|
2
1
|
class E
|
3
2
|
|
4
3
|
E404 = 'req/'+HTTP+'404'
|
@@ -16,8 +15,8 @@ r.map{|k,v| s[Header + k] = k == 'uri' ? v : [v] }
|
|
16
15
|
s[Header+'ACCEPT'] = [r.accept]
|
17
16
|
s[Type] = [E[HTTP+'Response']]
|
18
17
|
s[HTTP+'statusCodeValue'] = [404]
|
19
|
-
s[Header+'HTTP_HOST'] = [E['http://' + s[Header+'HTTP_HOST'][0]]]
|
20
|
-
s[Edit] = [E[r['REQUEST_PATH']+'?
|
18
|
+
s[Header+'HTTP_HOST'] = [E['http://' + s[Header+'HTTP_HOST'][0]]] if s[Header+'HTTP_HOST']
|
19
|
+
s[Edit] = [E[r['REQUEST_PATH']+'?graph=edit']]
|
21
20
|
s['#query'] = [r.q]
|
22
21
|
s['#seeAlso'] = [e.parent,*e.a('*').glob]
|
23
22
|
r.q['view'] = '404'
|
@@ -28,21 +27,10 @@ s[HTTP+'statusCodeValue'] = [404]
|
|
28
27
|
[H.css('/css/404'),{_: :style, c: "a {background-color:#{E.cs}}"},
|
29
28
|
d.html]}
|
30
29
|
|
31
|
-
# a
|
30
|
+
# a placeholder graph
|
31
|
+
# useful for initializing an editor on an empty resource
|
32
32
|
fn 'protograph/_',->d,_,m{
|
33
33
|
m[d.uri] = {}
|
34
34
|
rand.to_s.h}
|
35
35
|
|
36
|
-
# check response-codes for a list of URIs (linebreak-separated *.u files)
|
37
|
-
def checkURIs
|
38
|
-
r = uris.select{|u|u.to_s.match /^http/}.map{|u|
|
39
|
-
c = [`curl -IsA 404? "#{u}"`.lines.to_a[0].match(/\d{3}/)[0].to_i,u] # HEAD
|
40
|
-
puts c.join ' '
|
41
|
-
c } # status, uri tuple
|
42
|
-
puts "\n\n"
|
43
|
-
r.map{|c|
|
44
|
-
# show anomalies
|
45
|
-
puts c.join(' ') unless c[0] == 200 }
|
46
|
-
end
|
47
|
-
|
48
36
|
end
|
data/infod/500.rb
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
#watch __FILE__
|
2
|
+
class E
|
3
|
+
|
4
|
+
Errors ||= {}
|
5
|
+
|
6
|
+
fn 'E500',->x,e{
|
7
|
+
stack = x.backtrace
|
8
|
+
$stderr.puts [500, e['REQUEST_URI'], x.class, x.message, stack[0]].join ' '
|
9
|
+
|
10
|
+
Errors[e['uri']] ||= {}
|
11
|
+
Errors[e['uri']][:time] = Time.now
|
12
|
+
Errors[e['uri']][:env] = [e, x.class.to_s, x.message, stack.join('<br>')]
|
13
|
+
|
14
|
+
[500,{'Content-Type'=>'text/html'},
|
15
|
+
[H[{_: :html,
|
16
|
+
c: [{_: :head,
|
17
|
+
c: [{_: :title, c: 500},(H.css '/css/500')]},
|
18
|
+
{_: :body,
|
19
|
+
c: [{_: :h1, c: 500},
|
20
|
+
{_: :table,
|
21
|
+
c: [{_: :tr,c: [{_: :td, c: {_: :b, c: x.class}},{_: :td, class: :space},{_: :td, class: :message, c: x.message.hrefs}]},
|
22
|
+
stack.map{|f| p = f.split /:/, 3
|
23
|
+
{_: :tr,
|
24
|
+
c: [{_: :td, class: :path, c: p[0].abbrURI},
|
25
|
+
{_: :td, class: :index, c: p[1]},
|
26
|
+
{_: :td, class: :context, c: (p[2]||'').hrefs}].cr}}.cr]}]}]}]]]}
|
27
|
+
|
28
|
+
F['/500/GET'] = ->e,r{H([Errors.sort_by{|u,r|r[:time]}.reverse.html,H.css('/css/500')]).hR}
|
29
|
+
F['/500/test/GET'] = ->e,r{1/0}
|
30
|
+
|
31
|
+
F['view/'+COGS+'Exception']=->e,r{
|
32
|
+
e.values.map{|e|
|
33
|
+
{style: 'border-radius:1em;background-color:#333;color:#eee;float:left;max-width:42em',
|
34
|
+
c: [{_: :h2, c: "<b style='background-color:#f00'>¿</b>"+(e[Title]||[]).to_s},
|
35
|
+
e[Content]||''
|
36
|
+
]}}}
|
37
|
+
|
38
|
+
# filesystem takes priority over this if it's found
|
39
|
+
fn '/css/500.css/GET',->e,r{
|
40
|
+
[200,{'Content-Type'=>'text/css'},["
|
41
|
+
body {margin:0; font-family: sans-serif; background-color:#fff; color:#000}
|
42
|
+
h1 {padding:.2em; background-color:#f00; color:#fff; margin:0}
|
43
|
+
div {display:inline}
|
44
|
+
table {border-spacing:0;margin:0}
|
45
|
+
b {background-color:#eee;color:#500;padding:.1em .3em .1em .3em}
|
46
|
+
.frag {font-weight:bold; color:#000; background-color:#{E.cs}}
|
47
|
+
td.space {background-color:#ddd}
|
48
|
+
td.message {background-color:#009;color:#fff}
|
49
|
+
td.path {text-align:right}
|
50
|
+
td.index {text-align:right;border-color:#000;border-width:0 0 .1em 0;border-style:dotted;background-color:#ddd;color:#000}
|
51
|
+
td.context {border-color:#ddd;border-width:0 0 .1em 0;border-style:dotted;padding:.15em}"]]}
|
52
|
+
|
53
|
+
end
|
data/infod/GET.rb
ADDED
@@ -0,0 +1,104 @@
|
|
1
|
+
class E
|
2
|
+
|
3
|
+
Apache = ENV['apache']
|
4
|
+
Nginx = ENV['nginx']
|
5
|
+
|
6
|
+
def GET
|
7
|
+
if reqFn = @r.q['y'].do{|r| F['req/'+r] }
|
8
|
+
# bespoke handler
|
9
|
+
reqFn[self,@r]
|
10
|
+
|
11
|
+
elsif file = [self,pathSegment].compact.find(&:f)
|
12
|
+
|
13
|
+
# file exists, but client might not accept its MIME, or want it transformed to another MIME
|
14
|
+
a = @r.accept.values.flatten
|
15
|
+
accepted = a.empty? || (a.member? file.mimeP) || (a.member? '*/*')
|
16
|
+
(!accepted || MIMEcook[file.mimeP] || @r.q.has_key?('view')) ?
|
17
|
+
resource : (file.env @r).getFile
|
18
|
+
|
19
|
+
else
|
20
|
+
resource
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def getFile
|
25
|
+
@r['ETag'] = [m,size].h
|
26
|
+
maybeSend mimeP,->{self},:link
|
27
|
+
end
|
28
|
+
|
29
|
+
def resource
|
30
|
+
# bubble up site then global tree until handled (false return-value to pass)
|
31
|
+
pathSegment.do{|path|
|
32
|
+
lambdas = path.cascade.map{|p| p.uri.t + 'GET' }
|
33
|
+
['http://'+@r['SERVER_NAME'],""].map{|h| lambdas.map{|p|
|
34
|
+
F[h + p].do{|fn| fn[self,@r].do{|r|
|
35
|
+
$stdout.puts [r[0],'http://'+@r['SERVER_NAME']+@r['REQUEST_URI'],@r['HTTP_USER_AGENT'],@r['HTTP_REFERER'],@r.format].join ' '
|
36
|
+
return r
|
37
|
+
}}}}}
|
38
|
+
response
|
39
|
+
end
|
40
|
+
|
41
|
+
def response
|
42
|
+
|
43
|
+
m = {}
|
44
|
+
|
45
|
+
# graph identity (model)
|
46
|
+
g = @r.q['graph']
|
47
|
+
graphID = (g && F['protograph/' + g] || F['protograph/'])[self,@r.q,m]
|
48
|
+
|
49
|
+
return F[E404][self,@r] if m.empty?
|
50
|
+
|
51
|
+
# response identity (view)
|
52
|
+
@r['ETag'] ||= [@r.q['view'].do{|v|F['view/' + v] && v}, graphID, @r.format, Watch].h
|
53
|
+
|
54
|
+
maybeSend @r.format, ->{
|
55
|
+
|
56
|
+
# response
|
57
|
+
r = E'/E/req/' + @r['ETag'].dive
|
58
|
+
if r.e # exists
|
59
|
+
r
|
60
|
+
else
|
61
|
+
|
62
|
+
# graph
|
63
|
+
c = E '/E/graph/' + graphID.dive
|
64
|
+
if c.e # exists
|
65
|
+
m = c.r true
|
66
|
+
else
|
67
|
+
# construct
|
68
|
+
(g && F['graph/' + g] || F['graph/'])[self, @r.q,m]
|
69
|
+
# cache
|
70
|
+
c.w m,true
|
71
|
+
end
|
72
|
+
# cache < construct
|
73
|
+
r.w render @r.format, m, @r
|
74
|
+
end }
|
75
|
+
end
|
76
|
+
|
77
|
+
def send?
|
78
|
+
!((m=@r['HTTP_IF_NONE_MATCH']) && m.strip.split(/\s*,\s*/).include?(@r['ETag']))
|
79
|
+
end
|
80
|
+
|
81
|
+
def maybeSend m, b, iR = false; c = 200
|
82
|
+
send? ? # does agent have this version?
|
83
|
+
b[].do{|b| # continue with response
|
84
|
+
|
85
|
+
h = {'Content-Type'=> m,
|
86
|
+
'ETag'=> @r['ETag']}
|
87
|
+
|
88
|
+
h.update({'Cache-Control' => 'no-transform'}) if m.match /^(audio|image|video)/ # already compresed
|
89
|
+
h.update({'Link' => '<' + @r['uri'] + '?view>; rel=meta'}) if iR # link to description
|
90
|
+
# h.update({'MS-Author-Via' => 'SPARQL'}) # authoring preference
|
91
|
+
|
92
|
+
# frontend-specific response handlers
|
93
|
+
b.class == E ? (Nginx ? # nginx chosen?
|
94
|
+
[c,h.update({'X-Accel-Redirect' => '/fs' + b.path}),[]] : # Nginx handler
|
95
|
+
Apache ? # Apache chosen?
|
96
|
+
[c,h.update({'X-Sendfile' => b.d}),[]] : # Apache handler
|
97
|
+
(r = Rack::File.new nil # Rack handler
|
98
|
+
r.instance_variable_set '@path',b.d # configure Rack response
|
99
|
+
r.serving(@r).do{|s,m,b|[(s == 200 ? c : s),m.update(h),b]})) :
|
100
|
+
[c, h, b]} : # normal response
|
101
|
+
[304,{},[]] # client has response
|
102
|
+
end
|
103
|
+
|
104
|
+
end
|
data/infod/HEAD.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
class E
|
2
|
+
|
3
|
+
def HEAD
|
4
|
+
self.GET.do{|s,h,b|[s,h,[]]}
|
5
|
+
end
|
6
|
+
|
7
|
+
def OPTIONS
|
8
|
+
[200,{},[]]
|
9
|
+
end
|
10
|
+
|
11
|
+
# HEAD response-codes on a (.u) list of URIs
|
12
|
+
def checkURIs
|
13
|
+
r = uris.select{|u|u.to_s.match /^http/}.map{|u|
|
14
|
+
c = [`curl -IsA 404? "#{u}"`.lines.to_a[0].match(/\d{3}/)[0].to_i,u] # HEAD
|
15
|
+
puts c.join ' '
|
16
|
+
c } # status, uri tuple
|
17
|
+
puts "\n\n"
|
18
|
+
r.map{|c|
|
19
|
+
# show anomalies
|
20
|
+
puts c.join(' ') unless c[0] == 200 }
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
data/infod/HTTP.rb
ADDED
@@ -0,0 +1,105 @@
|
|
1
|
+
#watch __FILE__
|
2
|
+
require 'rack'
|
3
|
+
|
4
|
+
class E
|
5
|
+
|
6
|
+
def E.call e
|
7
|
+
dev
|
8
|
+
e.extend Th
|
9
|
+
e['HTTP_X_FORWARDED_HOST'].do{|h| e['SERVER_NAME'] = h }
|
10
|
+
p = e['REQUEST_PATH'].force_encoding 'UTF-8'
|
11
|
+
|
12
|
+
uri = CGI.unescape((p.index(URIURL) == 0) ? p[URIURL.size..-1] : ('http://'+e['SERVER_NAME']+(p.gsub '+','%2B'))).E.env e
|
13
|
+
|
14
|
+
uri.inside ? ( e['uri'] = uri.uri
|
15
|
+
uri.send e.verb ) : [403,{},[]]
|
16
|
+
|
17
|
+
rescue Exception => x
|
18
|
+
F['E500'][x,e]
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
class String
|
24
|
+
|
25
|
+
# querystring parse
|
26
|
+
def qp
|
27
|
+
d={}
|
28
|
+
split(/&/).map{|e|
|
29
|
+
k,v=e.split(/=/,2).map{|x|
|
30
|
+
CGI.unescape x}
|
31
|
+
d[k]=v}
|
32
|
+
d
|
33
|
+
end
|
34
|
+
|
35
|
+
def hR
|
36
|
+
[200, {'Content-Type'=>'text/html; charset=utf-8'}, [self]]
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
module Th
|
42
|
+
|
43
|
+
# query-string
|
44
|
+
def qs
|
45
|
+
(['GET','HEAD'].member? verb) ? self['QUERY_STRING'] : self['rack.input'].read
|
46
|
+
end
|
47
|
+
|
48
|
+
# parsed query-string
|
49
|
+
def q
|
50
|
+
@q ||= (qs||'').qp
|
51
|
+
end
|
52
|
+
|
53
|
+
# Accept header -> Hash
|
54
|
+
def accept_ k=''
|
55
|
+
d={}
|
56
|
+
self['HTTP_ACCEPT'+k].do{|k|
|
57
|
+
(k.split /,/).map{|e| # each pair
|
58
|
+
f,q = e.split /;/ # split MIME from q value
|
59
|
+
i = q && q.split(/=/)[1].to_f || 1.0
|
60
|
+
d[i] ||= []; d[i].push f.strip}} # append
|
61
|
+
d
|
62
|
+
end
|
63
|
+
|
64
|
+
def format
|
65
|
+
@format ||= conneg
|
66
|
+
end
|
67
|
+
|
68
|
+
def conneg
|
69
|
+
# extension
|
70
|
+
{ '.html' => 'text/html',
|
71
|
+
'.jsonld' => 'application/ld+json',
|
72
|
+
'.nt' => 'text/ntriples',
|
73
|
+
'.n3' => 'text/n3',
|
74
|
+
'.ttl' => 'text/turtle',
|
75
|
+
}[File.extname self['uri']].do{|mime|
|
76
|
+
return mime}
|
77
|
+
|
78
|
+
# Accept header
|
79
|
+
accept.sort.reverse.map{|q,mimes|
|
80
|
+
mimes.map{|mime|
|
81
|
+
return mime if E::F[E::Render+mime]}}
|
82
|
+
'text/html'
|
83
|
+
end
|
84
|
+
|
85
|
+
def accept; @accept ||= accept_ end
|
86
|
+
|
87
|
+
def verb
|
88
|
+
self['REQUEST_METHOD']
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|
92
|
+
|
93
|
+
class Hash
|
94
|
+
|
95
|
+
# unparse querystring
|
96
|
+
def qs
|
97
|
+
'?'+map{|k,v|k.to_s+'='+(v ? (CGI.escape [*v][0].to_s) : '')}.intersperse("&").join('')
|
98
|
+
end
|
99
|
+
|
100
|
+
def env
|
101
|
+
@r = r
|
102
|
+
self
|
103
|
+
end
|
104
|
+
|
105
|
+
end
|
File without changes
|
data/infod/POST.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
#watch __FILE__
|
2
|
+
class E
|
3
|
+
|
4
|
+
def POST
|
5
|
+
type = @r['CONTENT_TYPE']
|
6
|
+
case type
|
7
|
+
when /^application\/sparql-update/
|
8
|
+
|
9
|
+
when /^application\/x-www-form-urlencoded/
|
10
|
+
ch = nil
|
11
|
+
(Rack::Request.new @r).params.map{|k,v|
|
12
|
+
s, p, o = (CGI.unescape k).split S
|
13
|
+
if s && p && o
|
14
|
+
oP = o
|
15
|
+
s, p, o = [s, p, o].map &:unpath
|
16
|
+
s = s.uri[0..-2].E if s.uri[-1] == '/'
|
17
|
+
p = p.uri[0..-2].E if p.uri[-1] == '/'
|
18
|
+
if oP.E == (E.literal v) && s.uri.match(/^http/) && p.uri.match(/^http/)
|
19
|
+
puts "POST <#{s}> <#{p}> <#{o}>"
|
20
|
+
s[p,o,v]
|
21
|
+
ch = true
|
22
|
+
end
|
23
|
+
end}
|
24
|
+
if ch # state changed
|
25
|
+
g = {}
|
26
|
+
fromStream g, :triplrFsStore
|
27
|
+
ef.w g, true
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
[303,{'Location'=>uri},[]]
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
data/infod/audio.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
class E
|
2
|
+
|
3
|
+
VideoFile = /(avi|flv|mkv|mpg|mp4|wmv)$/i
|
4
|
+
AudioFile = /(aif|wav|flac|mp3|m4a|aac|ogg)$/i
|
5
|
+
|
6
|
+
fn 'set/audio',->d,e,m{d.take.select{|e|e.ext.match AudioFile}}
|
7
|
+
fn 'set/video',->d,e,m{d.take.select{|e|e.ext.match VideoFile}}
|
8
|
+
|
9
|
+
AudioK = {}
|
10
|
+
%w{Album-Movie-Show_title Lead_performers-Soloists Title-songname-content_description}.
|
11
|
+
map{|a|Audio + a}.concat(['uri', Stat+'mtime', Stat+'size']).map{|p|AudioK[p] = true}
|
12
|
+
|
13
|
+
fn 'view/audio',->d,e{ d = d.dup
|
14
|
+
|
15
|
+
# skip non-audio files
|
16
|
+
d.delete_if{|p,o|
|
17
|
+
(p.respond_to? :match) &&
|
18
|
+
(!p.match AudioFile)}
|
19
|
+
|
20
|
+
# select data-fields
|
21
|
+
d.values.map{|r|
|
22
|
+
r.class==Hash &&
|
23
|
+
r.delete_if{|p,o|!AudioK[p]}}
|
24
|
+
|
25
|
+
[(H.once e, :mu, (H.js '/js/mu')),(H.once e, :audio,(H.js '/js/audio'),(H.css '/css/audio'),
|
26
|
+
{id: :rand, c: :r}, {id: :jump, c: '→'}, {id: :info, target: :_blank, _: :a},
|
27
|
+
{_: e.q.has_key?('video') ? :video : :audio, id: :media, controls: true}), '<br>',
|
28
|
+
F['view/table'][d,e]]}
|
29
|
+
|
30
|
+
end
|