improved grid-layout for openbrush plugin
This commit is contained in:
parent
e1ea486212
commit
86eaf1556f
23 changed files with 635 additions and 146 deletions
|
|
@ -14,7 +14,7 @@ HidePath('/usr/share/ssl/')
|
||||||
|
|
||||||
-- create
|
-- create
|
||||||
app = require("soakbean") {
|
app = require("soakbean") {
|
||||||
cache = { items = {}, url = {}, shared = require('sharedcache') },
|
cache = {}, -- populated by reset()
|
||||||
plugin = {},
|
plugin = {},
|
||||||
opts = {},
|
opts = {},
|
||||||
forkpids = {},
|
forkpids = {},
|
||||||
|
|
@ -25,37 +25,47 @@ app = require("soakbean") {
|
||||||
createplugin = {file="cmd/createplugin.lua", info="generates skeleton plugin"},
|
createplugin = {file="cmd/createplugin.lua", info="generates skeleton plugin"},
|
||||||
generate = {file="cmd/generate.lua", info="generate www-folder with XR experiences"},
|
generate = {file="cmd/generate.lua", info="generate www-folder with XR experiences"},
|
||||||
test = {file="cmd/test.lua", info="run testsuite (selftest)"}
|
test = {file="cmd/test.lua", info="run testsuite (selftest)"}
|
||||||
},
|
}
|
||||||
|
|
||||||
addPlugin = function(file)
|
|
||||||
local p = require(file)
|
|
||||||
table.insert(app.plugin,p)
|
|
||||||
-- ensure plugin config-defaults
|
|
||||||
foreach( app.plugin, function(name,plugin)
|
|
||||||
foreach( plugin.opts, function(k,opt)
|
|
||||||
local key = opt.key:gsub("app.opts.","")
|
|
||||||
if not app.opts[ key ] then
|
|
||||||
app.opts[ key ] = opt.default
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
end)
|
|
||||||
app.set('app.opts', app.opts)
|
|
||||||
return p
|
|
||||||
end,
|
|
||||||
|
|
||||||
addExperience = function(experience, ino)
|
|
||||||
if app.cache.items[ino] then -- handle duplicate symlinks, symlinks to symlinks e.g.
|
|
||||||
app.cache.items[ino .. ' ' .. experience.parent.id ] = experience
|
|
||||||
else
|
|
||||||
app.cache.items[ino] = experience
|
|
||||||
end
|
|
||||||
end
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
app.reset = function()
|
||||||
|
app.cache = { items = {}, url = {}, shared = require('sharedcache'), dirs={} }
|
||||||
|
end
|
||||||
|
|
||||||
|
app.addPlugin = function(file)
|
||||||
|
local p = require(file)
|
||||||
|
table.insert(app.plugin,p)
|
||||||
|
-- ensure plugin config-defaults
|
||||||
|
foreach( app.plugin, function(name,plugin)
|
||||||
|
foreach( plugin.opts, function(k,opt)
|
||||||
|
local key = opt.key:gsub("app.opts.","")
|
||||||
|
if not app.opts[ key ] then
|
||||||
|
app.opts[ key ] = opt.default
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
app.set('app.opts', app.opts)
|
||||||
|
return p
|
||||||
|
end
|
||||||
|
|
||||||
|
app.addExperience = function(experience, ino)
|
||||||
|
if app.cache.items[ino] then -- handle duplicate symlinks, symlinks to symlinks e.g.
|
||||||
|
app.cache.items[ino .. ' ' .. experience.parent.id ] = experience
|
||||||
|
else
|
||||||
|
app.cache.items[ino] = experience
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
app.markViewable = function(exp)
|
||||||
|
exp.view3D = true
|
||||||
|
exp.parent.redirect = exp.url -- mark parent to redirect to this standalone url instead (not glb assets e.g.)
|
||||||
|
end
|
||||||
|
|
||||||
-- api skeleton (implemented via soakbean's app.on(...))
|
-- api skeleton (implemented via soakbean's app.on(...))
|
||||||
app.set = function(k,v) end
|
app.set = function(k,v) end
|
||||||
app.init = function() end
|
app.init = function()
|
||||||
|
app.reset()
|
||||||
|
end
|
||||||
|
|
||||||
-- load config
|
-- load config
|
||||||
app.opts = require('config')(app)
|
app.opts = require('config')(app)
|
||||||
|
|
@ -80,7 +90,7 @@ app.plugin.git = app.addPlugin('plugin/git')
|
||||||
app.plugin.webhook = app.addPlugin('plugin/webhook')
|
app.plugin.webhook = app.addPlugin('plugin/webhook')
|
||||||
app.plugin.archive = app.addPlugin('plugin/archive')
|
app.plugin.archive = app.addPlugin('plugin/archive')
|
||||||
app.plugin.openbrush = app.addPlugin('plugin/openbrush')
|
app.plugin.openbrush = app.addPlugin('plugin/openbrush')
|
||||||
--app.plugin.scheduler = app.addPlugin('plugin/scheduler') -- should always be second last
|
app.plugin.scheduler = app.addPlugin('plugin/scheduler') -- should always be second last
|
||||||
app.plugin.staticwebgen = app.addPlugin('plugin/staticwebgen') -- should always be last
|
app.plugin.staticwebgen = app.addPlugin('plugin/staticwebgen') -- should always be last
|
||||||
|
|
||||||
if unix.getpid() == 0 then unix.exit(0) end -- child-threads shall not pass beyond here
|
if unix.getpid() == 0 then unix.exit(0) end -- child-threads shall not pass beyond here
|
||||||
|
|
|
||||||
24
src/.lua/apiSync.lua
Normal file
24
src/.lua/apiSync.lua
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
local apiSync = {
|
||||||
|
|
||||||
|
syncHashtags = function(opts)
|
||||||
|
local synced = 0
|
||||||
|
local tags = opts.tags
|
||||||
|
if #tags == 0 then table.insert(tags,'') end
|
||||||
|
foreach( tags, function(k,tag)
|
||||||
|
local url = opts.apiCall( #tag > 0 and tag or nil)
|
||||||
|
opts.body = nil -- erase previous url result
|
||||||
|
print('🤞 fetch ' .. url)
|
||||||
|
local t = opts.fetch(url,opts)
|
||||||
|
if t then
|
||||||
|
synced = synced + opts.download(t,tag)
|
||||||
|
end
|
||||||
|
end, true)
|
||||||
|
if synced > 0 then
|
||||||
|
app.plugin.diskscan.scan(app.opts.plugin_diskscan_dir)
|
||||||
|
end
|
||||||
|
return synced
|
||||||
|
end
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return apiSync
|
||||||
|
|
@ -234,9 +234,15 @@ util.call = function(fn,a,b,c,d,e,f)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function foreach(t, f, sort)
|
function foreach(t, f, alwayscontinue)
|
||||||
if t ~= nil then
|
if t ~= nil then
|
||||||
for k, v in pairs(t) do f(k, v) end
|
for k, v in pairs(t) do
|
||||||
|
if alwayscontinue then
|
||||||
|
local success, err = pcall(f, k, v)
|
||||||
|
else
|
||||||
|
f(k, v)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -410,5 +416,41 @@ function util.traverseXML( parsedXml, cb)
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
util.hyphenate = function(str)
|
||||||
|
local str,match = str:lower()
|
||||||
|
:gsub("[^%w]", "-")
|
||||||
|
:gsub("%-+", "-")
|
||||||
|
:gsub("^%-", "")
|
||||||
|
:gsub("%-$", "")
|
||||||
|
return str
|
||||||
|
end
|
||||||
|
|
||||||
|
util.dehyphenate = function(str)
|
||||||
|
local str,match = str:gsub("_[a-zA-Z0-9]+","") -- remove ids starting with _
|
||||||
|
:gsub("%-%-","-") -- remove double dash
|
||||||
|
:gsub("-"," ") -- convert to spaces
|
||||||
|
:gsub("^%s*(.-)%s*$", "%1") -- trim
|
||||||
|
return str
|
||||||
|
end
|
||||||
|
|
||||||
|
util.stripId = function(path)
|
||||||
|
return path:gsub("_[a-zA-Z0-9]+",""), path:match("_[a-zA-Z0-9]+")
|
||||||
|
end
|
||||||
|
|
||||||
|
util.parseIsoDate = function(iso_str)
|
||||||
|
--local iso_str = "2017-11-03T08:04:45.076Z"
|
||||||
|
local year, month, day, hour, min, sec, ms = iso_str:match("(%d+)-(%d+)-(%d+)T(%d+):(%d+):(%d+)%.(%d+)Z")
|
||||||
|
|
||||||
|
year, month, day = tonumber(year), tonumber(month), tonumber(day)
|
||||||
|
hour, min, sec = tonumber(hour), tonumber(min), tonumber(sec)
|
||||||
|
ms = tonumber("0." .. ms) -- Optional: keep as decimal seconds fraction
|
||||||
|
|
||||||
|
local local_time = os.time({year=year, month=month, day=day, hour=hour, min=min, sec=sec})
|
||||||
|
local utc_offset = os.difftime(local_time, os.time(os.date("!*t", local_time)))
|
||||||
|
local timestamp = local_time + utc_offset
|
||||||
|
|
||||||
|
return timestamp, ms
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
return util
|
return util
|
||||||
|
|
|
||||||
BIN
src/img/banner.jpg
Normal file
BIN
src/img/banner.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 210 KiB |
BIN
src/img/layout_grid.jpg
Normal file
BIN
src/img/layout_grid.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 136 KiB |
BIN
src/img/layout_portal.jpg
Normal file
BIN
src/img/layout_portal.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 94 KiB |
|
|
@ -109,6 +109,7 @@ div#xrecosystem{
|
||||||
end}
|
end}
|
||||||
-->
|
-->
|
||||||
<br>
|
<br>
|
||||||
|
<img src="img/banner.jpg" style="width:100%"/>
|
||||||
<br>
|
<br>
|
||||||
|
|
||||||
<h3>Why people want XRForge</h3>
|
<h3>Why people want XRForge</h3>
|
||||||
|
|
@ -216,12 +217,50 @@ div#xrecosystem{
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="spectrum">
|
||||||
|
<div></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<h3 id="autocompose">Auto-compose virtual worlds</h3>
|
||||||
|
|
||||||
|
Instead of manually composing virtual worlds from multiple assets, XRForge's philosophy is <b>auto-layout</b> and <b>interoperability</b>.<br>
|
||||||
|
This means that assets produced in <a href="https://openblocks.app" target="_blank">OpenBlocks</a> or <a href="https://openbrush.app" target="_blank">OpenBrush</a> can <b>(in)directly</b> get imported</b> (via online 3D stores like <a href="https://icosa.gallery" target="_blank">icosa.gallery-</a> and <a href="https://manyfold.app" target="_blank">manyfold.app</a>).<br>
|
||||||
|
This allows for infinitely scalable virtual worlds, yet completely downloadable and portable.<br>
|
||||||
|
XRForge features <b>layout-engines</b> which allows <b>crowdsourcing virtual worlds</b> via assets using various apps/platforms:<br>
|
||||||
|
<br>
|
||||||
|
<img src="img/layout_grid.jpg" style="width:100%; max-width:1024px;"/>
|
||||||
|
<br><br>
|
||||||
|
<img src="img/layout_portal.jpg" style="width:100%; max-width:1024px"/>
|
||||||
|
<br>
|
||||||
|
<br>
|
||||||
|
|
||||||
|
An example:<br>
|
||||||
|
|
||||||
|
<blockquote>
|
||||||
|
1. designers agree on a hashtag (#myproject e.g)<br>
|
||||||
|
2. add the hashtag to the asset-title (in openbrush/openblocks/manyfold e.g.)<br>
|
||||||
|
3. add the hashtag to XRForge (in the respective plugin)<br>
|
||||||
|
4. specify the layout (grid, portals e.g.) for rendering the assets<br>
|
||||||
|
4. XRForge will pull or reference all hashtag-related assets<br>
|
||||||
|
5. Profit! XRForge will now automatically render a virtual world<br>
|
||||||
|
</blockquote>
|
||||||
|
<br>
|
||||||
|
|
||||||
|
By default all scenes are layered on top of eachother, which is great for horizon, sky etc.<br>
|
||||||
|
However, auto-placement in a grid (buildings e.g.) is possible by adding a dimensional hashtag (<b>#2x2</b> e.g.) to the asset-title.
|
||||||
|
</blockquote>
|
||||||
|
<br>
|
||||||
|
<br>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<div class="spectrum">
|
<div class="spectrum">
|
||||||
<div></div>
|
<div></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<center>
|
<center>
|
||||||
<h3>Comparison</h3>
|
<h2>Comparison</h2>
|
||||||
<table class="table table-dark table-striped" id="comparison">
|
<table class="table table-dark table-striped" id="comparison">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
|
|
|
||||||
|
|
@ -52,7 +52,7 @@ return {
|
||||||
if v.dir or v.view3D then
|
if v.dir or v.view3D then
|
||||||
local parent = app.cache.items[ v.parent.id ]
|
local parent = app.cache.items[ v.parent.id ]
|
||||||
res.items[ v.id ] = v
|
res.items[ v.id ] = v
|
||||||
res.itemdir = v
|
res.itemdir = v.parent
|
||||||
res.parent = parent
|
res.parent = parent
|
||||||
end
|
end
|
||||||
if isroot then
|
if isroot then
|
||||||
|
|
@ -68,6 +68,7 @@ return {
|
||||||
local data = util.merge({
|
local data = util.merge({
|
||||||
janusurl = req.param.url, -- query hint to render janus.url in iframe (index.html)
|
janusurl = req.param.url, -- query hint to render janus.url in iframe (index.html)
|
||||||
items = {},
|
items = {},
|
||||||
|
itemdir = res.itemdir,
|
||||||
isroot = isroot,
|
isroot = isroot,
|
||||||
referrer = referrer,
|
referrer = referrer,
|
||||||
req = req,
|
req = req,
|
||||||
|
|
@ -98,6 +99,7 @@ return {
|
||||||
-- what to do here? no generated portals?
|
-- what to do here? no generated portals?
|
||||||
data.janusxr = app.tpl( util.readfile(v.path), data )
|
data.janusxr = app.tpl( util.readfile(v.path), data )
|
||||||
end
|
end
|
||||||
|
print_r(data.item)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
{if true then
|
{if true then
|
||||||
sidecarfile = (parent and parent.path or '') .. '/' .. app.opts.plugin_janusxr_wrap_custom
|
wrapperfile = (parent and parent.path or '') .. '/' .. app.opts.plugin_janusxr_wrap_custom
|
||||||
hasSidecarfile = util.fileExist(sidecarfile) and true or false
|
hasWrapperfile = util.fileExist(wrapperfile) and true or false
|
||||||
showfediportals = app.opts.plugin_federation_strategy == 'always' or
|
showfediportals = app.opts.plugin_federation_strategy == 'always' or
|
||||||
(app.opts.plugin_federation_strategy == 'homepage' and urlpath == '/')
|
(app.opts.plugin_federation_strategy == 'homepage' and urlpath == '/')
|
||||||
|
|
||||||
|
|
@ -47,11 +47,17 @@ end}
|
||||||
if not defined(item) and defined(items) then
|
if not defined(item) and defined(items) then
|
||||||
room = app.opts.plugin_janusxr_portalroom
|
room = app.opts.plugin_janusxr_portalroom
|
||||||
end
|
end
|
||||||
if defined(item) and item.janusxr and item.janusxr.wrap then
|
|
||||||
pos = '0 0 2'
|
function initRoom(i)
|
||||||
room = app.plugin.janusxr.wrapRoomAroundFile(item)
|
if i.janusxr and i.janusxr.room then
|
||||||
|
pos = '0 0 1' -- todo make startup location configurable
|
||||||
|
room = i.janusxr.room
|
||||||
|
end
|
||||||
end
|
end
|
||||||
if hasSidecarfile or (defined(item) and item.tag.xrfragments and item.tag.xrfragments.href) then
|
initRoom(item or {})
|
||||||
|
initRoom(itemdir or {})
|
||||||
|
|
||||||
|
if (hasWrapperfile == true) or (defined(item) and item.tag.xrfragments and item.tag.xrfragments.href) then
|
||||||
room = '' -- xrfragments 3D files dont need to be wrapped with room
|
room = '' -- xrfragments 3D files dont need to be wrapped with room
|
||||||
end
|
end
|
||||||
print(' <room skybox="true" pos="' .. pos .. '" showavatar="false" use_local_asset="' .. room .. '">')
|
print(' <room skybox="true" pos="' .. pos .. '" showavatar="false" use_local_asset="' .. room .. '">')
|
||||||
|
|
@ -60,50 +66,27 @@ end}
|
||||||
end
|
end
|
||||||
end}
|
end}
|
||||||
<light js_id="dan9-janus:light/light_cone_angle=0-6" pos="0 3 2" collision_trigger="false" />
|
<light js_id="dan9-janus:light/light_cone_angle=0-6" pos="0 3 2" collision_trigger="false" />
|
||||||
{if true then
|
|
||||||
function addPortal(id,x,v,url,i,z,thumb,rotation,scale)
|
|
||||||
local title = v.title
|
|
||||||
print('\t<object js_id="' .. id .. '_' .. i .. '" pos="' .. x .. ' 0 ' .. z .. '" rotation="' .. rotation .. '">')
|
|
||||||
print('\t <link url="' .. util.baseURLify(url) .. '" ' .. thumb .. ' title_col="0.4 0.37 0.4" pos="0 0 0.001" draw_glow="' .. (thumb:match("shader") and 'false' or 'true') .. '" scale="3 3 0.1" />')
|
|
||||||
print('\t <text text="' .. util.truncate(title,20) .. '" pos="0 3.4 0" col="1 1 1" font_scale="false" scale="' .. scale .. '" />')
|
|
||||||
--TODO: fix janusweb paragraph for markdown urls
|
|
||||||
--if v.README ~= nil then
|
|
||||||
-- print('\t <paragraph js_id="README" url="' .. v.README .. '" width="600" height="900" pos="0 1 2" transparent="true"/>')
|
|
||||||
--end
|
|
||||||
print('\t</object>')
|
|
||||||
end
|
|
||||||
end}
|
|
||||||
<text js_id="location" pos="0 6 -5" billboard="y" col="#BBFFFF" scale="10 10 1" text="${url}" emissive="0 0 0" roughness="0.3" metalness="0" />
|
<text js_id="location" pos="0 6 -5" billboard="y" col="#BBFFFF" scale="10 10 1" text="${url}" emissive="0 0 0" roughness="0.3" metalness="0" />
|
||||||
{if not defined(item) and defined(items) and #items > 0 then
|
{if not defined(item) and defined(items) and #items > 0 then
|
||||||
for i,v in pairs(items) do
|
local layout = itemdir.layout or ''
|
||||||
local width = 3.2
|
if #layout > 0 and app.plugin.janusxr.layout[ layout ] then
|
||||||
local center = -((#items*width)/1.3) + (i*width)
|
print( app.plugin.janusxr.layout[ layout ].render(items) )
|
||||||
local url = v.url
|
else
|
||||||
local thumb = ' shader_id="defaultportal"'
|
print( app.plugin.janusxr.layout.portals.render(items) )
|
||||||
if #v.thumbnail > 0 then
|
|
||||||
thumb = ' thumb_id="img' .. i .. '" '
|
|
||||||
end
|
|
||||||
if v.dir then url = url .. "/" end
|
|
||||||
if v.redirect then url = v.redirect end -- dont wrap standalone glb's in janusxr
|
|
||||||
addPortal('dir',center, v, url,i, -4, thumb, '0 0 0', '0.2 0.2 0.2')
|
|
||||||
end
|
end
|
||||||
end}
|
end}
|
||||||
{if not defined(item) and defined(itemsremote) and #itemsremote > 0 then
|
{if not defined(item) and defined(itemsremote) and #itemsremote > 0 then
|
||||||
for i,v in pairs(itemsremote) do
|
for i,v in pairs(itemsremote) do
|
||||||
local width = 3.2
|
local width = v.portal_width or tonumber( app.opts.plugin_global_portal_width )
|
||||||
local center = -((#itemsremote*width)/1.3) + (i*width)
|
local center = -((#itemsremote*width)/1.3) + (i*width)
|
||||||
local url = v.url
|
local url = v.url
|
||||||
--if url:match("://") then
|
local thumb = ' shader_id="defaultportal"'
|
||||||
-- url = url .. ( url:match("#") and '&' or '#' ) .. 'janus.referrer=' ..
|
|
||||||
-- EscapeFragment( util.getURL() )
|
|
||||||
--end
|
|
||||||
local thumb = ' shader_id="defaultportal"'
|
|
||||||
if #v.thumbnail > 0 then
|
if #v.thumbnail > 0 then
|
||||||
thumb = ' thumb_id="imgremote' .. i .. '" '
|
thumb = ' thumb_id="imgremote' .. i .. '" '
|
||||||
end
|
end
|
||||||
if v.dir then url = url .. "/" end
|
if v.dir then url = url .. "/" end
|
||||||
if v.redirect then url = v.redirect end -- dont wrap standalone glb's in janusxr
|
if v.redirect then url = v.redirect end -- dont wrap standalone glb's in janusxr
|
||||||
addPortal('remote',center, v, url,i, 4, thumb, '0 180 0', '0.2 0.2 0.2')
|
print( app.plugin.janusxr.addPortal('remote',center, v, url,i, 4, thumb, '0 180 0', '0.2 0.2 0.2') )
|
||||||
end
|
end
|
||||||
print([[
|
print([[
|
||||||
<text js_id="locationremote" pos="0 6 5" billboard="y" col="#BBFFFF" scale="10 10 1" text="${opts.plugin_global_remote_title}" emissive="0 0 0" roughness="0.3" metalness="0" />
|
<text js_id="locationremote" pos="0 6 5" billboard="y" col="#BBFFFF" scale="10 10 1" text="${opts.plugin_global_remote_title}" emissive="0 0 0" roughness="0.3" metalness="0" />
|
||||||
|
|
@ -116,10 +99,10 @@ end}
|
||||||
url = referrer,
|
url = referrer,
|
||||||
title = app.opts.plugin_janusxr_portal_up,
|
title = app.opts.plugin_janusxr_portal_up,
|
||||||
}
|
}
|
||||||
addPortal('parent',5, v, v.url, 0, 0, ' shader_id="defaultportal"', '0 -90 0', '0.2 0.2 0.2')
|
print( app.plugin.janusxr.addPortal('parent',5, v, v.url, 0, 0, ' shader_id="defaultportal"', '0 -90 0', '0.2 0.2 0.2') )
|
||||||
end}
|
end}
|
||||||
|
|
||||||
{if isroot then
|
{if os.getenv('DEBUG') then
|
||||||
|
|
||||||
local plugins = {}
|
local plugins = {}
|
||||||
foreach(app.plugin, function(name,p)
|
foreach(app.plugin, function(name,p)
|
||||||
|
|
@ -150,12 +133,13 @@ end}
|
||||||
end}
|
end}
|
||||||
|
|
||||||
<object id="cube" rotation="0 45 0" pos="${pos}" scale="2 0.1 2"/> <!-- something to stand on -->
|
<object id="cube" rotation="0 45 0" pos="${pos}" scale="2 0.1 2"/> <!-- something to stand on -->
|
||||||
{if not defined(item) and not hasSidecarfile then
|
{if not defined(item) and hasWrapperfile == false then
|
||||||
print('${janusxr_jml_deco}')
|
print('${janusxr_jml_deco}')
|
||||||
end}
|
end}
|
||||||
{if hasSidecarfile then
|
{if hasWrapperfile == true then
|
||||||
|
local wrapperURL = util.stripPath( parent.path .. '/' .. app.opts.plugin_janusxr_wrap_custom)
|
||||||
print(' <!-- inserted by XRForge server: ' .. app.opts.plugin_janusxr_wrap_custom .. ' -->')
|
print(' <!-- inserted by XRForge server: ' .. app.opts.plugin_janusxr_wrap_custom .. ' -->')
|
||||||
print(' <link url="' .. parent.path .. '/' .. app.opts.plugin_janusxr_wrap_custom .. '" url_pos="0 0 0" pos="0 0 1" url_scale="1 1 1" js_id="index_jml" auto_load="true" />')
|
print(' <link url="' .. wrapperURL .. '" url_pos="0 0 0" pos="0 0 1" url_scale="1 1 1" js_id="index_jml" auto_load="true" />')
|
||||||
|
|
||||||
|
|
||||||
end}
|
end}
|
||||||
|
|
|
||||||
|
|
@ -45,13 +45,13 @@ local plugin = {
|
||||||
title='When to trigger',
|
title='When to trigger',
|
||||||
info='when should archive.org check this xrforge?',
|
info='when should archive.org check this xrforge?',
|
||||||
default = "boot",
|
default = "boot",
|
||||||
values={disabled="disabled", daily="daily"}
|
values={disabled="disabled", boot="on boot", daily="daily"}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key='app.opts.plugin_archive_url',
|
key='app.opts.plugin_archive_url',
|
||||||
title='Startingpoint URL',
|
title='Startingpoint URL',
|
||||||
info='absolute URL which archive.org (waybackmachine) should start with',
|
info='absolute URL which archive.org (waybackmachine) should start with',
|
||||||
default = "/myverse",
|
default = "",
|
||||||
placeholder = "/myverse",
|
placeholder = "/myverse",
|
||||||
values=''
|
values=''
|
||||||
},
|
},
|
||||||
|
|
@ -104,6 +104,7 @@ local plugin = {
|
||||||
|
|
||||||
start = function()
|
start = function()
|
||||||
local me = app.plugin.archive.scraper
|
local me = app.plugin.archive.scraper
|
||||||
|
os.execute('rm scraper.txt 2>&1')
|
||||||
me.scraperStart = os.time()
|
me.scraperStart = os.time()
|
||||||
util.fetchServer('/', nil, function(req,res)
|
util.fetchServer('/', nil, function(req,res)
|
||||||
me.scrapeLinks( req,res, 0 )
|
me.scrapeLinks( req,res, 0 )
|
||||||
|
|
@ -127,7 +128,7 @@ local plugin = {
|
||||||
:gsub("#.*",'')
|
:gsub("#.*",'')
|
||||||
)
|
)
|
||||||
if not util.fileExist( newdir ) then
|
if not util.fileExist( newdir ) then
|
||||||
util.execute('janusxr scrape "' .. node.prop.url .. '" "' .. newdir .. '" 1>&2')
|
util.execute('janusxr scrape "' .. node.prop.url .. '" "' .. newdir .. '" 1>&2 | tee -a scraper.txt')
|
||||||
else
|
else
|
||||||
print("already scraped: " .. node.prop.url )
|
print("already scraped: " .. node.prop.url )
|
||||||
end
|
end
|
||||||
|
|
@ -136,7 +137,6 @@ local plugin = {
|
||||||
end,
|
end,
|
||||||
|
|
||||||
scan = {
|
scan = {
|
||||||
|
|
||||||
JML = function(req,res)
|
JML = function(req,res)
|
||||||
local me = app.plugin.archive.scraper
|
local me = app.plugin.archive.scraper
|
||||||
-- JML heuristic
|
-- JML heuristic
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,7 @@ return {
|
||||||
end,
|
end,
|
||||||
|
|
||||||
scan = function(scandir)
|
scan = function(scandir)
|
||||||
|
app.reset() -- flush cache
|
||||||
local me = app.plugin.diskscan
|
local me = app.plugin.diskscan
|
||||||
local seen = {symlinks={}}
|
local seen = {symlinks={}}
|
||||||
-- ensure rootdir as experience
|
-- ensure rootdir as experience
|
||||||
|
|
@ -51,7 +52,7 @@ return {
|
||||||
url = "/" .. util.stripPath(scandir),
|
url = "/" .. util.stripPath(scandir),
|
||||||
name = scandir:gsub(".*/",""),
|
name = scandir:gsub(".*/",""),
|
||||||
thumbnail = "",
|
thumbnail = "",
|
||||||
title = scandir:gsub(".*/",""):gsub("-"," "),
|
title = util.dehyphenate( scandir:gsub(".*/","") ),
|
||||||
file = { ino = scandir },
|
file = { ino = scandir },
|
||||||
id = scandir
|
id = scandir
|
||||||
},scandir)
|
},scandir)
|
||||||
|
|
@ -79,11 +80,14 @@ return {
|
||||||
v.file.state = nil
|
v.file.state = nil
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
table.sort( app.cache.dirs)
|
||||||
end,
|
end,
|
||||||
|
|
||||||
scanDir = function(scandir,parent,seen)
|
scanDir = function(scandir,parent,seen)
|
||||||
local me = app.plugin.diskscan
|
local me = app.plugin.diskscan
|
||||||
parent = parent or app.cache.items[scandir]
|
parent = parent or app.cache.items[scandir]
|
||||||
|
app.cache.dirs[ scandir ] = util.stripPath(scandir)
|
||||||
|
|
||||||
-- scan dirs
|
-- scan dirs
|
||||||
local scanFile = function(path,stat,state,name,kind,ino,off,attr,fullpath)
|
local scanFile = function(path,stat,state,name,kind,ino,off,attr,fullpath)
|
||||||
|
|
||||||
|
|
@ -91,7 +95,7 @@ return {
|
||||||
path = fullpath,
|
path = fullpath,
|
||||||
url = "/" .. util.stripPath(fullpath),
|
url = "/" .. util.stripPath(fullpath),
|
||||||
name = name,
|
name = name,
|
||||||
title = name:gsub("-"," "),
|
title = util.dehyphenate( name ),
|
||||||
sidecarfile = {},
|
sidecarfile = {},
|
||||||
thumbnail = '',
|
thumbnail = '',
|
||||||
tag = {},
|
tag = {},
|
||||||
|
|
|
||||||
|
|
@ -198,6 +198,14 @@ You've got this!
|
||||||
default="Remote / Federated",
|
default="Remote / Federated",
|
||||||
values=''
|
values=''
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
key='app.opts.plugin_global_portal_width',
|
||||||
|
title='Portal spacing',
|
||||||
|
info='Adjust spacing between portals (prevent thumbnail-overlap)',
|
||||||
|
placeholder = "3.2",
|
||||||
|
default = "3.2",
|
||||||
|
values=''
|
||||||
|
},
|
||||||
{
|
{
|
||||||
key='app.opts.plugin_global_backup_trigger',
|
key='app.opts.plugin_global_backup_trigger',
|
||||||
title='Backup',
|
title='Backup',
|
||||||
|
|
|
||||||
|
|
@ -29,14 +29,16 @@ app.on('scan_experience_file', function(exp)
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
if wrap then
|
if wrap then
|
||||||
exp.view3D = true
|
app.markViewable(exp)
|
||||||
exp.janusxr = { wrap = { object = true }}
|
exp.janusxr = { wrap = { object = true }}
|
||||||
|
exp.janusxr.room = me.wrapRoomAroundFile( exp )
|
||||||
|
app.pub("scan_experience_janusxr", nil, {exp=exp, item=item}) -- be nice to other plugins
|
||||||
app.addExperience( exp, exp.file.ino )
|
app.addExperience( exp, exp.file.ino )
|
||||||
print("✅ └─ plugin/janusxr: wrapping " .. exp.path)
|
print("✅ └─ plugin/janusxr: wrapping " .. exp.path)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if me.isWrapFile(exp.path) and size < 100000 then -- skip large binary files > 100kb
|
if (not me.isWrapFile(exp.path)) and size < 100000 then -- skip large binary files > 100kb
|
||||||
local data = util.readfile( exp.file.fullpath)
|
local data = util.readfile( exp.file.fullpath)
|
||||||
if data then
|
if data then
|
||||||
if data:match(pattern_fireboxroom) or
|
if data:match(pattern_fireboxroom) or
|
||||||
|
|
@ -45,16 +47,15 @@ app.on('scan_experience_file', function(exp)
|
||||||
if title then
|
if title then
|
||||||
exp.title = title:gsub("<.*","")
|
exp.title = title:gsub("<.*","")
|
||||||
end
|
end
|
||||||
app.pub("scan_experience_janusxr", nil, {exp=exp, item=item}) -- be nice to other plugins
|
app.markViewable(exp)
|
||||||
app.addExperience( exp, exp.file.ino )
|
|
||||||
exp.view3D = true
|
|
||||||
exp.parent.redirect = exp.url -- mark parent to redirect to this standalone url instead (not glb assets e.g.)
|
|
||||||
exp.janusxr = exp.janusxr or {}
|
exp.janusxr = exp.janusxr or {}
|
||||||
print("✅ └─ plugin/janusxr: " .. exp.file.name)
|
print("✅ └─ plugin/janusxr: " .. exp.file.name)
|
||||||
if data:match(pattern_aframe) and app.opts.plugin_janusxr_wrap_aframe == 'enabled' then
|
if data:match(pattern_aframe) and app.opts.plugin_janusxr_wrap_aframe == 'enabled' then
|
||||||
exp.janusxr.wrap = {aframe = true }
|
exp.janusxr.wrap = {aframe = true }
|
||||||
print("✅ └─ plugin/janusxr: AFRAME markup detected")
|
print("✅ └─ plugin/janusxr: AFRAME markup detected")
|
||||||
end
|
end
|
||||||
|
app.pub("scan_experience_janusxr", nil, {exp=exp, item=item}) -- be nice to other plugins
|
||||||
|
app.addExperience( exp, exp.file.ino )
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
-- symlink to directory
|
-- symlink to directory
|
||||||
|
|
@ -65,7 +66,7 @@ end)
|
||||||
app.on('set', function(k,v) end)
|
app.on('set', function(k,v) end)
|
||||||
|
|
||||||
-- this registers the plugin UI
|
-- this registers the plugin UI
|
||||||
local rooms = {random="random room",randomurl="random room (URL-deterministic)",room_plane="wirefloor",room1="room1",room2="room2",room3="room3",room4="room4",room5="room5",room6="room6",room7="room7"}
|
local rooms = { [""]="empty",random="random room",randomurl="random room (URL-deterministic)",room_plane="wirefloor",room1="room1",room2="room2",room3="room3",room4="room4",room5="room5",room6="room6",room7="room7"}
|
||||||
|
|
||||||
local plugin = {
|
local plugin = {
|
||||||
name = "JanusXR",
|
name = "JanusXR",
|
||||||
|
|
@ -91,44 +92,44 @@ local plugin = {
|
||||||
{
|
{
|
||||||
key='app.opts.plugin_janusxr_wrap_custom',
|
key='app.opts.plugin_janusxr_wrap_custom',
|
||||||
title='VR wrap custom',
|
title='VR wrap custom',
|
||||||
info='include "index.jml" files (if found) in generated JML',
|
info='include "wrapper.jml" file (if present) in generated JML',
|
||||||
default="index.jml",
|
default="wrapper.jml",
|
||||||
values={ ["index.jml"] = "index.jml"}
|
values={ ["wrapper.jml"] = "wrapper.jml"}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key='app.opts.plugin_janusxr_wrap_3dfile',
|
key='app.opts.plugin_janusxr_wrap_3dfile',
|
||||||
title='VR wrap 3D file',
|
title='VR wrap 3D file',
|
||||||
info='render 3D files in <a href="https://coderofsalvation.github.io/janus-guide/#/build/room" target="_blank">janusxr.xml</a> room',
|
info='render 3D files in <a href="https://coderofsalvation.github.io/janus-guide/#/build/room" target="_blank">janusxr.xml</a> room',
|
||||||
default="randomurl",
|
default="randomurl",
|
||||||
values=util.merge(rooms,{disabled="-- disabled --"})
|
values=rooms
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key='app.opts.plugin_janusxr_wrap_image',
|
key='app.opts.plugin_janusxr_wrap_image',
|
||||||
title='VR wrap images',
|
title='VR wrap images',
|
||||||
info='render images in <a href="https://coderofsalvation.github.io/janus-guide/#/build/room" target="_blank">janusxr.xml</a> room',
|
info='render images in <a href="https://coderofsalvation.github.io/janus-guide/#/build/room" target="_blank">janusxr.xml</a> room',
|
||||||
default="randomurl",
|
default="randomurl",
|
||||||
values=util.merge(rooms,{disabled="-- disabled --"})
|
values=rooms
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key='app.opts.plugin_janusxr_wrap_video',
|
key='app.opts.plugin_janusxr_wrap_video',
|
||||||
title='VR wrap videos',
|
title='VR wrap videos',
|
||||||
info='render videos in <a href="https://coderofsalvation.github.io/janus-guide/#/build/room" target="_blank">janusxr.xml</a> room',
|
info='render videos in <a href="https://coderofsalvation.github.io/janus-guide/#/build/room" target="_blank">janusxr.xml</a> room',
|
||||||
default="randomurl",
|
default="randomurl",
|
||||||
values=util.merge(rooms,{disabled="-- disabled --"})
|
values=rooms
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key='app.opts.plugin_janusxr_wrap_aframe',
|
key='app.opts.plugin_janusxr_wrap_aframe',
|
||||||
title='AFRAME support',
|
title='AFRAME support',
|
||||||
info='render <a href="https://aframe.io">AFRAME</a> (geometry) markup when detected in URLs',
|
info='render <a href="https://aframe.io">AFRAME</a> (geometry) markup when detected in URLs',
|
||||||
default="room_plane",
|
default="room_plane",
|
||||||
values=util.merge(rooms,{disabled="-- disabled --"})
|
values=rooms
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key='app.opts.plugin_janusxr_portalroom',
|
key='app.opts.plugin_janusxr_portalroom',
|
||||||
title='Directory room',
|
title='Directory room',
|
||||||
info='What kind of space should directories be rendered into?',
|
info='What kind of space should directories be rendered into?',
|
||||||
default="room_plane",
|
default="room_plane",
|
||||||
values={random="random room",room_plane="wirefloor",room1="room1",room2="room2",room3="room3",room4="room4",room5="room5",room6="room6",room7="room7"}
|
values=rooms
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key='app.opts.plugin_janusxr_portal_up',
|
key='app.opts.plugin_janusxr_portal_up',
|
||||||
|
|
@ -178,9 +179,27 @@ local plugin = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
layout = {
|
||||||
|
portals = require('plugin/janusxr/layout/portals'),
|
||||||
|
grid = require('plugin/janusxr/layout/grid')
|
||||||
|
},
|
||||||
|
|
||||||
|
addPortal = function(id,x,v,url,i,z,thumb,rotation,scale)
|
||||||
|
local title = v.title
|
||||||
|
local jml = ''
|
||||||
|
jml = jml .. '\t<object js_id="' .. id .. '_' .. i .. '" pos="' .. x .. ' 0 ' .. z .. '" rotation="' .. rotation .. '">\n'
|
||||||
|
jml = jml .. '\t <link url="' .. util.baseURLify(url) .. '" ' .. thumb .. ' title_col="0.4 0.37 0.4" pos="0 0 0.001" draw_glow="' .. (thumb:match("shader") and 'false' or 'true') .. '" scale="3 3 0.1" />\n'
|
||||||
|
jml = jml .. '\t <text text="' .. util.truncate(title,20) .. '" pos="0 3.4 0" col="1 1 1" font_scale="false" scale="' .. scale .. '" />\n'
|
||||||
|
--TODO: fix janusweb paragraph for markdown urls
|
||||||
|
if v.README ~= nil then
|
||||||
|
jml = jml .. '\t <paragraph js_id="README" url="' .. v.README .. '" width="600" height="900" pos="0 1 2" transparent="true"/>\n'
|
||||||
|
end
|
||||||
|
jml = jml .. '\t</object>\n'
|
||||||
|
return jml
|
||||||
|
end,
|
||||||
|
|
||||||
isWrapFile = function(path)
|
isWrapFile = function(path)
|
||||||
return tostring(path):match("/" .. app.opts.plugin_janusxr_wrap_custom:gsub("%.",".") .. "jml$")
|
return tostring(path):match( app.opts.plugin_janusxr_wrap_custom )
|
||||||
and true or false
|
|
||||||
end,
|
end,
|
||||||
|
|
||||||
wrapRoomAroundFile = function(item)
|
wrapRoomAroundFile = function(item)
|
||||||
|
|
@ -194,6 +213,7 @@ local plugin = {
|
||||||
if room == 'randomurl' then
|
if room == 'randomurl' then
|
||||||
room = app.plugin.janusxr.getRandomRoom(item.url)
|
room = app.plugin.janusxr.getRandomRoom(item.url)
|
||||||
end
|
end
|
||||||
|
if room == 'disabled' then room = '' end
|
||||||
return room
|
return room
|
||||||
end,
|
end,
|
||||||
|
|
||||||
|
|
|
||||||
69
src/plugin/janusxr/layout/grid.lua
Normal file
69
src/plugin/janusxr/layout/grid.lua
Normal file
|
|
@ -0,0 +1,69 @@
|
||||||
|
local grid = {
|
||||||
|
|
||||||
|
--render = function(items)
|
||||||
|
-- local jml = ''
|
||||||
|
-- local gridsize = tonumber( app.opts.plugin_openbrush_layout_grid_size )
|
||||||
|
-- for i,v in pairs(items) do
|
||||||
|
-- local url = v.redirect and v.redirect or v.url
|
||||||
|
-- local pos = {x=0,y=0,z=0}
|
||||||
|
-- local posStr = pos.x .. ' ' .. pos.y .. ' ' .. pos.z
|
||||||
|
-- jml = jml .. ' <link url="' .. url .. '" url_pos="' .. posStr .. '" url_scale="1 1 1" auto_load="true"/>\n'
|
||||||
|
-- end
|
||||||
|
-- return jml
|
||||||
|
--end
|
||||||
|
|
||||||
|
render = function(items)
|
||||||
|
local jml = ''
|
||||||
|
-- Distance between items in meters. gridsize = 5 means 5m x 5m spacing.
|
||||||
|
local gridsize = tonumber(app.opts.plugin_openbrush_layout_grid_size) or 5
|
||||||
|
-- Square spiral: Start at (0, 0) right, up, left, down, increasing the step length
|
||||||
|
local x = 0
|
||||||
|
local z = 0
|
||||||
|
local dx = 1
|
||||||
|
local dz = 0
|
||||||
|
local steps = 1
|
||||||
|
local stepsTaken = 0
|
||||||
|
local turns = 0
|
||||||
|
local index = 0
|
||||||
|
for i, v in pairs(items) do
|
||||||
|
local url = v.redirect and v.redirect or v.url
|
||||||
|
-- Position current item
|
||||||
|
local pos = {
|
||||||
|
x = v.title:match("#overlay") and 0 or x * gridsize,
|
||||||
|
y = 0,
|
||||||
|
z = v.title:match("#overlay") and 0 or z * gridsize
|
||||||
|
}
|
||||||
|
local posStr = pos.x .. ' ' .. pos.y .. ' ' .. pos.z
|
||||||
|
local is_nested_room = false
|
||||||
|
if is_nested_room then
|
||||||
|
jml = jml .. ' <link url="' .. url .. '" url_pos="' .. posStr
|
||||||
|
.. '" url_scale="1 1 1" auto_load="true"/>\n'
|
||||||
|
else
|
||||||
|
jml = jml .. ' <object id="' .. url .. '" pos="' .. posStr .. '"/>\n'
|
||||||
|
end
|
||||||
|
index = index + 1
|
||||||
|
-- Move to next position in the spiral
|
||||||
|
if index < #items then
|
||||||
|
x = x + dx
|
||||||
|
z = z + dz
|
||||||
|
stepsTaken = stepsTaken + 1
|
||||||
|
if stepsTaken == steps then
|
||||||
|
stepsTaken = 0
|
||||||
|
-- Rotate clockwise: right -> up -> left -> down
|
||||||
|
local oldDx = dx
|
||||||
|
dx = -dz
|
||||||
|
dz = oldDx
|
||||||
|
turns = turns + 1
|
||||||
|
-- Increase the length after every second turn
|
||||||
|
if turns % 2 == 0 then
|
||||||
|
steps = steps + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return jml
|
||||||
|
end
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return grid
|
||||||
22
src/plugin/janusxr/layout/portals.lua
Normal file
22
src/plugin/janusxr/layout/portals.lua
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
local portals = {
|
||||||
|
render = function(items)
|
||||||
|
local jml = ''
|
||||||
|
for i,v in pairs(items) do
|
||||||
|
local width = v.portal_width or tonumber(app.opts.plugin_global_portal_width )
|
||||||
|
local center = -((#items*width)/1.3) + (i*width)
|
||||||
|
local url = v.url
|
||||||
|
local thumb = ' shad..er_id="defaultportal"'
|
||||||
|
if #v.thumbnail > 0 then
|
||||||
|
thumb = ' thumb_id="img' .. i .. '" '
|
||||||
|
end
|
||||||
|
if v.dir then url = url .. "/" end
|
||||||
|
if v.redirect then url = v.redirect end -- dont wrap standalone glb's in janusxr
|
||||||
|
jml = jml
|
||||||
|
.. app.plugin.janusxr.addPortal('dir',center, v, url,i, -4, thumb, '0 0 0', '0.2 0.2 0.2')
|
||||||
|
.. '\n'
|
||||||
|
end
|
||||||
|
return jml
|
||||||
|
end
|
||||||
|
}
|
||||||
|
|
||||||
|
return portals
|
||||||
|
|
@ -1,21 +1,69 @@
|
||||||
local util = require('util')
|
local util = require('util')
|
||||||
|
local apiSync = require('apiSync')
|
||||||
|
|
||||||
|
local syncIcosaGallery = function()
|
||||||
|
if #app.opts.plugin_openbrush_sync_URL > 0 then
|
||||||
|
local synced = apiSync.syncHashtags({
|
||||||
|
tags = util.split( app.opts.plugin_openbrush_sync_hashtag or '', ' '),
|
||||||
|
apiCall = app.plugin.openbrush.apiCall,
|
||||||
|
fetch = app.plugin.openbrush.fetch,
|
||||||
|
download = app.plugin.openbrush.download
|
||||||
|
})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
app.on('init', function()
|
app.on('init', function()
|
||||||
end)
|
foreach( app.plugin.openbrush.opts, function(k,opt)
|
||||||
|
if opt.key == 'app.opts.plugin_openbrush_sync_folder' then
|
||||||
app.on('scan_experience', function(exp)
|
opt.values = app.cache.dirs
|
||||||
end)
|
end
|
||||||
|
end)
|
||||||
app.on('scan_experience_file', function(exp)
|
if util.contains(argv, 'generate') then
|
||||||
end)
|
syncIcosaGallery()
|
||||||
|
end
|
||||||
app.on('set', function(k,v)
|
|
||||||
end)
|
end)
|
||||||
|
|
||||||
app.on('scheduler', function(opts)
|
app.on('scheduler', function(opts)
|
||||||
-- if opts.interval == app.opts.plugin_archive_when then
|
if util.contains(argv, 'server') then
|
||||||
--
|
if util.cmdExist('wget') and opts.interval == app.opts.plugin_openbrush_sync_when then
|
||||||
-- end
|
syncIcosaGallery()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
local function scanTiltFile(exp)
|
||||||
|
if exp.path:lower():match(".tilt$") then
|
||||||
|
app.markViewable(exp)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function scanForOpenBrush(exp)
|
||||||
|
local tags = app.plugin.openbrush.tags
|
||||||
|
if not tags then
|
||||||
|
tags = util.split( app.opts.plugin_openbrush_sync_hashtag, ' ')
|
||||||
|
table.insert( tags,"openbrush")
|
||||||
|
app.plugin.openbrush.tags = tags
|
||||||
|
end
|
||||||
|
foreach( tags, function(k,tag)
|
||||||
|
tag = tag:gsub("#","")
|
||||||
|
if exp.path:match('/' .. tag .. '/') then -- might not be precise enough
|
||||||
|
if not exp.parent.janusxr then
|
||||||
|
exp.parent.janusxr = {}
|
||||||
|
end
|
||||||
|
exp.parent.janusxr.room = app.opts.plugin_openbrush_wrap_room
|
||||||
|
exp.parent.portal_width = tonumber( app.opts.plugin_openbrush_portal_width )
|
||||||
|
exp.parent.layout = app.opts.plugin_openbrush_layout
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
scanTiltFile(exp)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
app.on('scan_experience', scanForOpenBrush )
|
||||||
|
app.on('scan_experience_file', scanForOpenBrush )
|
||||||
|
app.on("scan_experience_janusxr", function(opts) scanForOpenBrush(opts.exp) end)
|
||||||
|
|
||||||
|
app.on('set', function(k,v)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
local sync_URL = app.opts_plugin_openbrush_sync_URL or ''
|
local sync_URL = app.opts_plugin_openbrush_sync_URL or ''
|
||||||
|
|
@ -28,38 +76,91 @@ local plugin = {
|
||||||
<img src="img/openbrush.png" style="width:50%; max-width:233px; display:inline-block; float:left; margin-right:20px;"/>
|
<img src="img/openbrush.png" style="width:50%; max-width:233px; display:inline-block; float:left; margin-right:20px;"/>
|
||||||
<a href="https://openbrush.app/" target="_blank">OpenBrush</a> lets you paint in 3D space with virtual reality.<br>
|
<a href="https://openbrush.app/" target="_blank">OpenBrush</a> lets you paint in 3D space with virtual reality.<br>
|
||||||
XRForge's webviewer (<a href="https://github.com/jbaicoianu/janusweb">JanusWeb</a>) supports interconnecting OpenBrush projects via 3D portals.<br/>
|
XRForge's webviewer (<a href="https://github.com/jbaicoianu/janusweb">JanusWeb</a>) supports interconnecting OpenBrush projects via 3D portals.<br/>
|
||||||
OpenBrush projects are stored in a (selfhosted) <a href="https://icosa.gallery" target="_blank">icosa.gallery</a> instance, which can be configured below.
|
OpenBrush projects are stored in a (selfhosted) <a href="https://icosa.gallery" target="_blank">icosa.gallery</a> instance, which can be configured below.<br><br>
|
||||||
|
<b>NOTE:</b> for collective virtualworld building use <b>hashtags in project-titles</b>. Each hashtag will become its own folder, which allows for <a href="/about#autocompose">crowdsourcing/combining scenes</a> designed in openbrush.
|
||||||
]],
|
]],
|
||||||
opts = {
|
opts = {
|
||||||
{
|
{
|
||||||
key='app.opts.plugin_openbrush_sync_when',
|
key='app.opts.plugin_openbrush_wrap_room',
|
||||||
title='When to sync',
|
title='Wrap VR space',
|
||||||
info='when to check for new experiences?',
|
info='render 3D files in <a href="https://coderofsalvation.github.io/janus-guide/#/build/room" target="_blank">janusxr.xml</a> room',
|
||||||
default = "hourly",
|
default="room_plane",
|
||||||
values={disabled="disabled", daily="daily", hourly="hourly"}
|
values= app.plugin.janusxr.opts[4].values -- reuse rooms from janusxr plugin
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key='app.opts.plugin_openbrush_sync_folder_thumbnail',
|
key='app.opts.plugin_openbrush_layout',
|
||||||
title='Sync to folder',
|
title='Layout assets',
|
||||||
info='openbrush (icosa.gallery) assets will be saved here',
|
info='NOTE: use #overlay in asset-title to skip layout (sky/floor/e.g.)',
|
||||||
placeholder = "openbrush",
|
default="portals",
|
||||||
default = "openbrush",
|
values={portals="As portals", grid="As grid"}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key='app.opts.plugin_openbrush_layout_grid_size',
|
||||||
|
title='Grid size',
|
||||||
|
info='in square meters (unused for non-grid layout)',
|
||||||
|
default="3",
|
||||||
|
placeholder="3",
|
||||||
values=''
|
values=''
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
key='app.opts.plugin_openbrush_sync_strategy',
|
||||||
|
title='How to sync',
|
||||||
|
info='mirroring = most resilient; references = instant updates',
|
||||||
|
default = "mirror",
|
||||||
|
values={mirror="mirror locally",reference="reference remote URL"}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key='app.opts.plugin_openbrush_sync_when',
|
||||||
|
title='Sync interval',
|
||||||
|
info='when to sync?',
|
||||||
|
default = "boot",
|
||||||
|
values={disabled="disabled", boot="on boot", hourly="hourly", daily="daily"}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key='app.opts.plugin_openbrush_sync_bruteforce',
|
||||||
|
title='Sync anyways',
|
||||||
|
info='ignore timestamps',
|
||||||
|
default = "enabled",
|
||||||
|
values={disabled="disabled", enabled="enabled"}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
key='app.opts.plugin_openbrush_sync_folder',
|
key='app.opts.plugin_openbrush_sync_folder',
|
||||||
|
title='Sync to folder',
|
||||||
|
info='openbrush (icosa.gallery) assets will be saved here',
|
||||||
|
placeholder = "openbrush",
|
||||||
|
default = app.opts.plugin_diskscan_dir,
|
||||||
|
values = {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key='app.opts.plugin_openbrush_sync_folder_thumbnail',
|
||||||
title='Thumbnail',
|
title='Thumbnail',
|
||||||
info='URL of Thumbnail for folder/portal',
|
info='URL of Thumbnail for folder/portal',
|
||||||
placeholder = "img/openbrush.png",
|
placeholder = "img/openbrush.png",
|
||||||
default = "img/openbrush.png",
|
default = "img/openbrush.png",
|
||||||
values=''
|
values=''
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
key='app.opts.plugin_openbrush_portal_width',
|
||||||
|
title='Portal spacing',
|
||||||
|
info='Adjust spacing between portals (prevent thumbnail-overlap)',
|
||||||
|
placeholder = "4.2",
|
||||||
|
default = "4.2",
|
||||||
|
values=''
|
||||||
|
},
|
||||||
{
|
{
|
||||||
key='app.opts.plugin_openbrush_sync_URL',
|
key='app.opts.plugin_openbrush_sync_URL',
|
||||||
title='Icosa gallery URL',
|
title='Icosa gallery URL',
|
||||||
info='public/selfhosted <a href="https://github.com/icosa-foundation/icosa-gallery target="_blank">icosa.gallery</a> server-instance',
|
info='public/selfhosted <a href="https://github.com/icosa-foundation/icosa-gallery target="_blank">icosa.gallery</a> server-instance',
|
||||||
placeholder = "https://icosa.gallery",
|
placeholder = "icosa.gallery",
|
||||||
default = "https://icosa.gallery",
|
default = "icosa.gallery",
|
||||||
|
values=''
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key='app.opts.plugin_openbrush_sync_hashtag',
|
||||||
|
title='tags',
|
||||||
|
info=( sync_URL and 'space-separated tags will generate their own folders' or ''),
|
||||||
|
default = "#1 #xrforge",
|
||||||
|
placeholder = "#1 #xrforge",
|
||||||
values=''
|
values=''
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -78,14 +179,6 @@ local plugin = {
|
||||||
default = "",
|
default = "",
|
||||||
values=''
|
values=''
|
||||||
},
|
},
|
||||||
{
|
|
||||||
key='app.opts.plugin_openbrush_sync_tag',
|
|
||||||
title='tags',
|
|
||||||
info=( sync_URL and 'space-separated, leave empty or see <a href="https://api.' .. sync_URL .. '/v1/docs#/Assets/icosa_api_assets_get_assets">API docs</a>' or ''),
|
|
||||||
placeholder = "xrforge",
|
|
||||||
default = "",
|
|
||||||
values=''
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
key='app.opts.plugin_openbrush_sync_triangle_max',
|
key='app.opts.plugin_openbrush_sync_triangle_max',
|
||||||
title='Polygons maximum',
|
title='Polygons maximum',
|
||||||
|
|
@ -97,7 +190,7 @@ local plugin = {
|
||||||
{
|
{
|
||||||
key='app.opts.plugin_openbrush_sync_author',
|
key='app.opts.plugin_openbrush_sync_author',
|
||||||
title='Author name',
|
title='Author name',
|
||||||
info='filter assets on specific Author name',
|
info='space-separated: filter assets on specific Author name',
|
||||||
placeholder='sarah',
|
placeholder='sarah',
|
||||||
default = "",
|
default = "",
|
||||||
values=""
|
values=""
|
||||||
|
|
@ -105,19 +198,132 @@ local plugin = {
|
||||||
{
|
{
|
||||||
key='app.opts.plugin_openbrush_sync_authorid',
|
key='app.opts.plugin_openbrush_sync_authorid',
|
||||||
title='Author id',
|
title='Author id',
|
||||||
info='filter assets on specific Author id',
|
info='space-separated: filter assets on specific Author id',
|
||||||
placeholder='9e8b98fbe',
|
placeholder='9e8b98fbe',
|
||||||
default = "",
|
default = "",
|
||||||
values=""
|
values=""
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
key='app.opts.plugin_openbrush_sync_maxitems',
|
||||||
|
title='Max latest items',
|
||||||
|
info='limit the total amount of result',
|
||||||
|
placeholder='3',
|
||||||
|
default = "3",
|
||||||
|
values=""
|
||||||
|
},
|
||||||
{
|
{
|
||||||
key='app.opts.plugin_openbrush_sync_collection',
|
key='app.opts.plugin_openbrush_sync_collection',
|
||||||
title='Collection required',
|
title='In collection',
|
||||||
info='should assets be part of a collection?',
|
info='should assets be part of a collection?',
|
||||||
default = "enabled",
|
default = "disabled",
|
||||||
values={enabled="enabled", disabled="disabled"}
|
values={enabled="yes", disabled="no"}
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
|
||||||
|
apiCall = function(tag)
|
||||||
|
return 'https://api.' .. app.opts.plugin_openbrush_sync_URL .. '/v1/assets?xrforge=' .. EscapeFragment(app.host)
|
||||||
|
.. '&xrforge_strategy=' .. EscapeFragment(app.opts.plugin_openbrush_sync_strategy)
|
||||||
|
.. (tag and #tag > 0 and '&keywords=' .. EscapeFragment(tag) or '')
|
||||||
|
.. (#app.opts.plugin_openbrush_sync_category > 0 and '&category=' .. EscapeFragment(app.opts.plugin_openbrush_sync_category) or '')
|
||||||
|
.. (#app.opts.plugin_openbrush_sync_keywords > 0 and '&keywords=' .. EscapeFragment(app.opts.plugin_openbrush_sync_keywords) or '')
|
||||||
|
.. (#app.opts.plugin_openbrush_sync_triangle_max > 0 and '&triangleCountMax=' .. EscapeFragment(app.opts.plugin_openbrush_sync_triangle_max) or '')
|
||||||
|
.. (app.opts.plugin_openbrush_sync_strategy == 'mirror' and '&license=ALL_CC' or '' )
|
||||||
|
.. (app.opts.plugin_openbrush_sync_collection == 'enabled' and '&inCollection=true' or '' )
|
||||||
|
.. (#app.opts.plugin_openbrush_sync_maxitems > 0 and '&pageSize=' .. EscapeFragment(app.opts.plugin_openbrush_sync_maxitems) or '' )
|
||||||
|
-- TODO: filter AFTER the request
|
||||||
|
-- .. (#app.opts.plugin_openbrush_sync_author > 0 and '&authorName=' .. EscapeFragment(app.opts.plugin_openbrush_sync_author) or '')
|
||||||
|
-- .. (#app.opts.plugin_openbrush_sync_authorid > 0 and '&authorId=' .. EscapeFragment(app.opts.plugin_openbrush_sync_authorid) or '')
|
||||||
|
end,
|
||||||
|
|
||||||
|
getFolder = function(tag)
|
||||||
|
local folder = app.opts.plugin_openbrush_sync_folder
|
||||||
|
if tag then
|
||||||
|
folder = folder .. '/' .. util.hyphenate(tag)
|
||||||
|
end
|
||||||
|
return folder
|
||||||
|
end,
|
||||||
|
|
||||||
|
fetch = function(url,opts)
|
||||||
|
opts = opts or {}
|
||||||
|
if not opts.body then
|
||||||
|
local ok, headers, body = Fetch( url )
|
||||||
|
if not ok then
|
||||||
|
return print("[openbrush.lua] error: could not fetch: " .. app.plugin.openbrush.apiCall(tag))
|
||||||
|
end
|
||||||
|
opts.body = body
|
||||||
|
end
|
||||||
|
return DecodeJson(opts.body)
|
||||||
|
end,
|
||||||
|
|
||||||
|
download = function(t,tag)
|
||||||
|
local me = app.plugin.openbrush
|
||||||
|
local synced = 0
|
||||||
|
foreach( t.assets, function(k,asset)
|
||||||
|
if type(asset) == 'table' then
|
||||||
|
local dirname = '_' .. asset.assetId .. '-' .. util.hyphenate( asset.displayName)
|
||||||
|
local dirnameFull = me.getFolder(tag) .. '/' .. dirname
|
||||||
|
local jsonfile = dirnameFull .. '/asset.json'
|
||||||
|
local sync = true
|
||||||
|
local assetPrevious = false
|
||||||
|
local f = false
|
||||||
|
|
||||||
|
function preferFormat(f)
|
||||||
|
local found = false
|
||||||
|
foreach( asset.formats, function(k,v)
|
||||||
|
if v.formatType == f then
|
||||||
|
found = v
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
return found
|
||||||
|
end
|
||||||
|
|
||||||
|
f = preferFormat('TILT')
|
||||||
|
if not f then f = preferFormat('GLTF2') end
|
||||||
|
if not f then f = preferFormat('GLTF1') end
|
||||||
|
if not f then f = preferFormat('OBJ') end
|
||||||
|
if f then
|
||||||
|
if not util.fileExist( dirnameFull ) then
|
||||||
|
os.execute("mkdir -p " .. dirnameFull)
|
||||||
|
end
|
||||||
|
|
||||||
|
if util.fileExist(jsonfile) then
|
||||||
|
assetPrevious = DecodeJson( util.readfile(jsonfile) )
|
||||||
|
if util.parseIsoDate(asset.updateTime) == util.parseIsoDate(assetPrevious.updateTime) then
|
||||||
|
sync = false -- already finished
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if sync or app.opts.plugin_openbrush_sync_bruteforce == 'enabled' then
|
||||||
|
util.writefile( jsonfile, EncodeJson(asset) )
|
||||||
|
local rootfile = dirnameFull .. '/' .. f.root.url:gsub(".*/","")
|
||||||
|
local thumbnailfile = dirnameFull .. '.' .. asset.thumbnail.url:gsub(".*%.",'')
|
||||||
|
if app.opts.plugin_openbrush_layout == 'portals' then
|
||||||
|
print('🤞 fetch ' .. asset.thumbnail.url)
|
||||||
|
os.execute('wget "' ..asset.thumbnail.url .. '" -O "' .. thumbnailfile .. '"')
|
||||||
|
end
|
||||||
|
print('🤞 fetch ' .. f.root.url)
|
||||||
|
os.execute('wget "' .. f.root.url .. '" -O "' .. rootfile .. '"')
|
||||||
|
if #f.resources > 0 then
|
||||||
|
foreach( f.resources, function(k,resource)
|
||||||
|
local file = dirnameFull .. '/' .. resource.url:gsub(".*/","")
|
||||||
|
print('🤞 fetch ' .. resource.url)
|
||||||
|
os.execute('wget "' .. resource.url .. '" -O "' .. file .. '"')
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
synced = synced + 1
|
||||||
|
else
|
||||||
|
print('[openbrush.lua] ' .. asset.displayName .. ' is up to date')
|
||||||
|
end
|
||||||
|
else
|
||||||
|
print('[openbrush.lua] error: no suitable format found for ' .. asset.displayName .. ' ' .. asset.assetId)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end, true)
|
||||||
|
return synced
|
||||||
|
end
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if not util.cmdExist('wget') then
|
||||||
|
plugin.info = plugin.info .. '<br><br><b>NOTE:</b> wget command not installed..plugin is disabled'
|
||||||
|
end
|
||||||
|
|
||||||
return plugin
|
return plugin
|
||||||
|
|
|
||||||
|
|
@ -16,8 +16,9 @@ return {
|
||||||
if pid == 0 then -- child has zero pid
|
if pid == 0 then -- child has zero pid
|
||||||
function periodically()
|
function periodically()
|
||||||
local check_step = os.getenv('DEBUG') and 1 or 5
|
local check_step = os.getenv('DEBUG') and 1 or 5
|
||||||
local elapsed = { min5 = 0, hourly = 0, daily = 0, daily2 = 0, weekly = 0}
|
local elapsed = { boot = 0, min5 = 0, hourly = 0, daily = 0, daily2 = 0, weekly = 0}
|
||||||
local intervals = {
|
local intervals = {
|
||||||
|
boot = 1,
|
||||||
min5 = 60*5, -- 5 mins worth seconds
|
min5 = 60*5, -- 5 mins worth seconds
|
||||||
hourly = 60*60, -- 1 hours worth of seconds
|
hourly = 60*60, -- 1 hours worth of seconds
|
||||||
daily = 60*60*24, -- 24 hours worth of seconds
|
daily = 60*60*24, -- 24 hours worth of seconds
|
||||||
|
|
|
||||||
|
|
@ -11,9 +11,15 @@ app.on('scan_experience_file', function(exp)
|
||||||
foreach( util.split( app.opts.plugin_xrfragments_filetypes, ' '), function(k,ext)
|
foreach( util.split( app.opts.plugin_xrfragments_filetypes, ' '), function(k,ext)
|
||||||
if exp.path:lower():match("."..ext.."$") then
|
if exp.path:lower():match("."..ext.."$") then
|
||||||
local hasHref = false
|
local hasHref = false
|
||||||
|
local tree = false
|
||||||
|
-- detect XR Fragment exp https://xrfragment.org/#%F0%9F%93%9C%20level0%3A%20File
|
||||||
if ext == 'glb' then
|
if ext == 'glb' then
|
||||||
-- detect XR Fragment exp https://xrfragment.org/#%F0%9F%93%9C%20level0%3A%20File
|
tree = me.extract_glb_json( exp.path ) --util.stripPath(exp.path) )
|
||||||
local tree = me.extract_glb_json( exp.path ) --util.stripPath(exp.path) )
|
end
|
||||||
|
if ext == 'gltf' then
|
||||||
|
tree = DecodeJson( util.readfile( exp.path ) )
|
||||||
|
end
|
||||||
|
if three then
|
||||||
hasHref = me.detectHrefInStruct(tree)
|
hasHref = me.detectHrefInStruct(tree)
|
||||||
end
|
end
|
||||||
-- check sidecarfiles: https://xrfragment.org/#sidecar%20files
|
-- check sidecarfiles: https://xrfragment.org/#sidecar%20files
|
||||||
|
|
@ -39,7 +45,7 @@ app.on('scan_experience_file', function(exp)
|
||||||
href = hasHref
|
href = hasHref
|
||||||
}
|
}
|
||||||
if exp.tag.xrfragments.qualify then
|
if exp.tag.xrfragments.qualify then
|
||||||
exp.view3D = true
|
app.markViewable(exp)
|
||||||
print("✅ └─ plugin/xrfragments: detected xrfragments experience")
|
print("✅ └─ plugin/xrfragments: detected xrfragments experience")
|
||||||
end
|
end
|
||||||
if exp.tag.xrfragments.href then
|
if exp.tag.xrfragments.href then
|
||||||
|
|
@ -66,7 +72,7 @@ return {
|
||||||
key='app.opts.plugin_xrfragments_filetypes',
|
key='app.opts.plugin_xrfragments_filetypes',
|
||||||
title='Filetypes',
|
title='Filetypes',
|
||||||
info='Space-separated 3D filetypes, <a href="http://lua-users.org/wiki/PatternsTutorial" target="_blank">lua patterns</a> are allowed',
|
info='Space-separated 3D filetypes, <a href="http://lua-users.org/wiki/PatternsTutorial" target="_blank">lua patterns</a> are allowed',
|
||||||
default="glb gltf obj dae",
|
default="glb gltf obj dae tilt",
|
||||||
values=''
|
values=''
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
||||||
1
src/test/json/icosagallery.json
Normal file
1
src/test/json/icosagallery.json
Normal file
File diff suppressed because one or more lines are too long
|
|
@ -5,10 +5,12 @@ local describe, it, expect = lust.describe, lust.it, lust.expect
|
||||||
|
|
||||||
lust.queueServerRequest = util.queueServerRequest
|
lust.queueServerRequest = util.queueServerRequest
|
||||||
|
|
||||||
require('test/plugin_global')(lust)
|
--require('test/plugin_global')(lust)
|
||||||
require('test/plugin_git')(lust)
|
--require('test/plugin_git')(lust)
|
||||||
require('test/plugin_diskscan')(lust)
|
--require('test/plugin_diskscan')(lust)
|
||||||
require('test/plugin_xrfragment')(lust)
|
--require('test/plugin_xrfragment')(lust)
|
||||||
|
require('test/plugin_openbrush')(lust)
|
||||||
|
require('test/util')(lust)
|
||||||
|
|
||||||
if lust.errors == 0 then
|
if lust.errors == 0 then
|
||||||
print("✅ all tests passed")
|
print("✅ all tests passed")
|
||||||
|
|
|
||||||
31
src/test/plugin_openbrush.lua
Normal file
31
src/test/plugin_openbrush.lua
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
return function(lust)
|
||||||
|
|
||||||
|
|
||||||
|
local describe, it, expect = lust.describe, lust.it, lust.expect
|
||||||
|
|
||||||
|
describe('src/plugin/openbrush.lua', function()
|
||||||
|
|
||||||
|
describe('get API url', function() -- Can be nested
|
||||||
|
local url = app.plugin.openbrush.apiCall()
|
||||||
|
expect( url:match('https://') ).to.be.truthy()
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe('parse icosa.gallery json', function() -- Can be nested
|
||||||
|
local json = util.readfile('/zip/test/json/icosagallery.json')
|
||||||
|
local opts = {body=json}
|
||||||
|
local t = app.plugin.openbrush.fetch('',opts)
|
||||||
|
expect( t.assets and #t.assets > 0 ).to.be.truthy()
|
||||||
|
end)
|
||||||
|
|
||||||
|
--describe('download icosa.gallery assets', function() -- Can be nested
|
||||||
|
-- local json = util.readfile('/zip/test/json/icosagallery.json')
|
||||||
|
-- local opts = {body=json}
|
||||||
|
-- foreach( app.plugin.openbrush.fetch(opts), function(i,t)
|
||||||
|
-- local res = app.plugin.openbrush.download(t)
|
||||||
|
-- expect( res ).to.be.truthy()
|
||||||
|
-- end)
|
||||||
|
--end)
|
||||||
|
|
||||||
|
end)
|
||||||
|
|
||||||
|
end
|
||||||
18
src/test/util.lua
Normal file
18
src/test/util.lua
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
return function(lust)
|
||||||
|
|
||||||
|
|
||||||
|
local describe, it, expect = lust.describe, lust.it, lust.expect
|
||||||
|
|
||||||
|
describe('src/.lua/util.lua', function()
|
||||||
|
|
||||||
|
describe('hyphenate()', function() -- Can be nested
|
||||||
|
expect( util.hyphenate(" foo bar") == "foo-bar" ).to.be.truthy()
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe('dehyphenate()', function() -- Can be nested
|
||||||
|
expect( util.dehyphenate("_e3f55-foo-bar-_438394") == "foo bar" ).to.be.truthy()
|
||||||
|
end)
|
||||||
|
|
||||||
|
end)
|
||||||
|
|
||||||
|
end
|
||||||
|
|
@ -54,7 +54,7 @@ build(){
|
||||||
|
|
||||||
rebuild(){
|
rebuild(){
|
||||||
cd src
|
cd src
|
||||||
zip -yr ../result/bin/xrforge.com .lua js shader img middleware page plugin cmd css .init.lua .args test myverse
|
zip -yr ../result/bin/xrforge.com .lua js shader img layout middleware page plugin cmd css .init.lua .args test myverse
|
||||||
}
|
}
|
||||||
|
|
||||||
"$@"
|
"$@"
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue