improved default popup for mobile + openbrush/manyfold centralized refactor
This commit is contained in:
parent
86eaf1556f
commit
81b9d238a9
13 changed files with 521 additions and 188 deletions
|
|
@ -31,6 +31,7 @@ app = require("soakbean") {
|
||||||
app.reset = function()
|
app.reset = function()
|
||||||
app.cache = { items = {}, url = {}, shared = require('sharedcache'), dirs={} }
|
app.cache = { items = {}, url = {}, shared = require('sharedcache'), dirs={} }
|
||||||
end
|
end
|
||||||
|
app.reset()
|
||||||
|
|
||||||
app.addPlugin = function(file)
|
app.addPlugin = function(file)
|
||||||
local p = require(file)
|
local p = require(file)
|
||||||
|
|
@ -50,7 +51,8 @@ end
|
||||||
|
|
||||||
app.addExperience = function(experience, ino)
|
app.addExperience = function(experience, ino)
|
||||||
if app.cache.items[ino] then -- handle duplicate symlinks, symlinks to symlinks e.g.
|
if app.cache.items[ino] then -- handle duplicate symlinks, symlinks to symlinks e.g.
|
||||||
app.cache.items[ino .. ' ' .. experience.parent.id ] = experience
|
local postfix = '_' .. ( experience.parent and experience.parent.id or tostring(os.time()) )
|
||||||
|
app.cache.items[ino .. postfix ] = experience
|
||||||
else
|
else
|
||||||
app.cache.items[ino] = experience
|
app.cache.items[ino] = experience
|
||||||
end
|
end
|
||||||
|
|
@ -63,9 +65,7 @@ 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()
|
app.init = function() end
|
||||||
app.reset()
|
|
||||||
end
|
|
||||||
|
|
||||||
-- load config
|
-- load config
|
||||||
app.opts = require('config')(app)
|
app.opts = require('config')(app)
|
||||||
|
|
@ -97,19 +97,16 @@ if unix.getpid() == 0 then unix.exit(0) end -- child-threads shall not pass beyo
|
||||||
|
|
||||||
app.runcmd(app) -- run clicmds
|
app.runcmd(app) -- run clicmds
|
||||||
|
|
||||||
print = function(...) Log(kLogInfo, ... or "''") end -- nice redbean logging from now on
|
print = function(...) Log(kLogInfo, ... or "''") end -- nice redbean logging from now on
|
||||||
|
|
||||||
-- init URL router
|
-- init URL router
|
||||||
app.url['^/data'] = '/data.lua' -- setup custom file endpoint
|
app.url['^/data'] = '/data.lua' -- setup custom file endpoint
|
||||||
|
|
||||||
app.use( app.plugin.global.middleware.homepage_2D_or_3D )
|
app.use( app.plugin.global.middleware.homepage_2D_or_3D )
|
||||||
app.url['^/admin[/]?$'] = '/page/admin/index.lua'
|
|
||||||
app.use( require('middleware/onlyGET')({exclude={'^/admin'}}) ) -- we are staticwebgen-first server-last
|
|
||||||
|
|
||||||
app.get('^/design[/]?$', util.pagerender('page/design.html'))
|
app.get('^/design[/]?$', util.pagerender('page/design.html'))
|
||||||
app.get('^/about[/]?$', util.pagerender('page/about.html'))
|
app.get('^/about[/]?$', util.pagerender('page/about.html'))
|
||||||
|
|
||||||
|
|
||||||
app.post('^/save', function(req,res,next) -- setup inline POST endpoint
|
app.post('^/save', function(req,res,next) -- setup inline POST endpoint
|
||||||
-- also .get(), .put(), .delete(), .options()
|
-- also .get(), .put(), .delete(), .options()
|
||||||
app.cache = req.body -- middleware auto-decodes json
|
app.cache = req.body -- middleware auto-decodes json
|
||||||
|
|
@ -125,7 +122,17 @@ app --
|
||||||
return user == app.opts.admin_user and pass == app.opts.admin_pass
|
return user == app.opts.admin_user and pass == app.opts.admin_pass
|
||||||
end
|
end
|
||||||
}))
|
}))
|
||||||
.use( require("json").middleware() ) -- try plug'n'play json API middleware
|
|
||||||
|
app.url['^/admin[/]?$'] = '/page/admin/index.lua'
|
||||||
|
app.use( require('middleware/onlyGET')({exclude={'^/admin'}}) ) -- we are staticwebgen-first server-last
|
||||||
|
app.get('/admin/server/reset', function(req,res,next) -- easy way to reload/rescan xrforge
|
||||||
|
app.reset()
|
||||||
|
res.status(303)
|
||||||
|
res.header('Location', req.header['Referer']:gsub("?.*","") .. '?notify=Server%20is%20being%20reloaded.' )
|
||||||
|
res.send(req,res)
|
||||||
|
end)
|
||||||
|
|
||||||
|
app.use( require("json").middleware() ) -- try plug'n'play json API middleware
|
||||||
.use( require('page/dir').middleware ) -- directory browser
|
.use( require('page/dir').middleware ) -- directory browser
|
||||||
.use( app.router( app.url ) ) -- try url router
|
.use( app.router( app.url ) ) -- try url router
|
||||||
.use( app.response() ) -- try serve app response (if any)
|
.use( app.response() ) -- try serve app response (if any)
|
||||||
|
|
@ -149,3 +156,10 @@ OnServerStart = function()
|
||||||
app.init() -- this triggers init event
|
app.init() -- this triggers init event
|
||||||
end
|
end
|
||||||
|
|
||||||
|
OnServerStop = function()
|
||||||
|
-- run the 'reset' cli-cmd equivalent (as childprocesses might leave
|
||||||
|
-- the terminal in a confused state)
|
||||||
|
io.write("\27[H\27[2J\27[3J\27[0m")
|
||||||
|
io.flush()
|
||||||
|
end
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,22 +1,76 @@
|
||||||
|
-- this is a helper/orchestrator for apicalls/syncs
|
||||||
|
--
|
||||||
|
-- implemented in openbrush.lua
|
||||||
|
|
||||||
local apiSync = {
|
local apiSync = {
|
||||||
|
|
||||||
syncHashtags = function(opts)
|
init = function(pluginname)
|
||||||
local synced = 0
|
local plugin = app.plugin[pluginname]
|
||||||
local tags = opts.tags
|
local api = {}
|
||||||
if #tags == 0 then table.insert(tags,'') end
|
api.pluginname = pluginname
|
||||||
foreach( tags, function(k,tag)
|
if not plugin.apiCall or not plugin.fetch or not plugin.download then
|
||||||
local url = opts.apiCall( #tag > 0 and tag or nil)
|
print("apiSync.lua: plugin " .. pluginname .. " does not implement the apiSync interface")
|
||||||
opts.body = nil -- erase previous url result
|
os.exit()
|
||||||
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
|
end
|
||||||
return synced
|
api.apiCall = plugin.apiCall
|
||||||
|
api.fetch = plugin.fetch
|
||||||
|
api.download = plugin.download
|
||||||
|
|
||||||
|
api.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.cache.url = {} -- delete page cache
|
||||||
|
app.plugin.diskscan.scan(app.opts.plugin_diskscan_dir)
|
||||||
|
end
|
||||||
|
return synced
|
||||||
|
end
|
||||||
|
|
||||||
|
api.sync = function(api)
|
||||||
|
if #app.opts['plugin_' .. pluginname .. '_sync_URL'] > 0 and util.cmdExist('wget') then
|
||||||
|
local synced = api.syncHashtags({
|
||||||
|
tags = util.split( app.opts['plugin_' .. api.pluginname .. '_sync_hashtag'] or '', ' '),
|
||||||
|
apiCall = api.apiCall,
|
||||||
|
fetch = api.fetch,
|
||||||
|
download = api.download
|
||||||
|
})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- runs sync in foreground
|
||||||
|
app.on('reset', function()
|
||||||
|
api.sync(api)
|
||||||
|
end)
|
||||||
|
|
||||||
|
-- runs sync in foreground
|
||||||
|
app.on('init', function()
|
||||||
|
foreach( app.plugin.openbrush.opts, function(k,opt)
|
||||||
|
if opt.key == 'app.opts.plugin_openbrush_sync_folder' then
|
||||||
|
opt.values = app.cache.dirs
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
if util.contains(argv, 'generate') then api.sync(api) end
|
||||||
|
end)
|
||||||
|
|
||||||
|
-- runs sync background
|
||||||
|
app.on('scheduler', function(opts)
|
||||||
|
if util.contains(argv, 'server') then
|
||||||
|
if opts.interval == app.opts['plugin_' .. pluginname .. '_sync_when'] then
|
||||||
|
api.sync(api)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
return api
|
||||||
end
|
end
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -57,6 +57,7 @@ td,div,p{
|
||||||
|
|
||||||
input,
|
input,
|
||||||
textarea,
|
textarea,
|
||||||
|
button,
|
||||||
select{
|
select{
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
margin: 0px 0px 10px 0px;
|
margin: 0px 0px 10px 0px;
|
||||||
|
|
@ -91,6 +92,7 @@ input[type=submit]{
|
||||||
.btn a:active,
|
.btn a:active,
|
||||||
.btn a:visited,
|
.btn a:visited,
|
||||||
.btn a:link,
|
.btn a:link,
|
||||||
|
button,
|
||||||
input[type=submit] {
|
input[type=submit] {
|
||||||
font-size:15px;
|
font-size:15px;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,9 @@ ${header}
|
||||||
<br/>
|
<br/>
|
||||||
<hr style="margin-top:0">
|
<hr style="margin-top:0">
|
||||||
|
|
||||||
${notification}
|
{if #notification > 0 then
|
||||||
|
print('<div class="notification">' .. notification .. '</div>')
|
||||||
|
end}
|
||||||
|
|
||||||
<form method="POST">
|
<form method="POST">
|
||||||
<div class="tab-frame">
|
<div class="tab-frame">
|
||||||
|
|
@ -19,6 +21,9 @@ ${notification}
|
||||||
<div class="btn">
|
<div class="btn">
|
||||||
<input type="submit" class="btn secondary" value="Save" style=""/>
|
<input type="submit" class="btn secondary" value="Save" style=""/>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="btn" style="float:right">
|
||||||
|
<a href="/admin/server/reset" class="btn primary">reload server</a>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
local util = require("util")
|
local util = require("util")
|
||||||
|
|
||||||
local data = {notification=''}
|
local data = {notification= GetParam('notify') or ''}
|
||||||
data.plugins = ""
|
data.plugins = ""
|
||||||
data.tabs = {}
|
data.tabs = {}
|
||||||
|
|
||||||
|
|
@ -87,9 +87,9 @@ function saveData()
|
||||||
end)
|
end)
|
||||||
if saveconfig then
|
if saveconfig then
|
||||||
app.set('app.opts', app.opts)
|
app.set('app.opts', app.opts)
|
||||||
data.notification = '<div class="notification">updated</div>'
|
data.notification = 'updated'
|
||||||
else
|
else
|
||||||
data.notification = '<div class="notification">no changes detected</div>'
|
data.notification = 'no changes detected'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,21 @@
|
||||||
]])
|
]])
|
||||||
end
|
end
|
||||||
end}
|
end}
|
||||||
<br/><br/>
|
<br/>
|
||||||
|
{if true then
|
||||||
|
local btns = plugin.buttons or {}
|
||||||
|
if #btns > 0 then
|
||||||
|
for i,btn in pairs(btns) do
|
||||||
|
print([[
|
||||||
|
<div class="btn">
|
||||||
|
<a href="]] .. btn.href .. [[" class="btn primary">]] .. (btn.label or '') .. [[</a>
|
||||||
|
</div>
|
||||||
|
]])
|
||||||
|
end
|
||||||
|
print('<br/>')
|
||||||
|
end
|
||||||
|
end}
|
||||||
|
<br/>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
${opts}
|
${opts}
|
||||||
|
|
|
||||||
|
|
@ -99,7 +99,6 @@ 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)
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,6 @@ 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
|
||||||
|
|
|
||||||
|
|
@ -98,51 +98,60 @@ local plugin = {
|
||||||
title='Viewer instructions',
|
title='Viewer instructions',
|
||||||
info='Default popup for 3D viewer instructions',
|
info='Default popup for 3D viewer instructions',
|
||||||
default = [[
|
default = [[
|
||||||
<h1>Tips to navigate in XR</h1>
|
<h1>Hi there! 👋</h1>
|
||||||
<table>
|
Welcome to this XR experience.<br/>
|
||||||
<tr>
|
<br/>
|
||||||
<td width="200">
|
<div class="btn script">
|
||||||
<b>AR/VR headset</b>
|
<button class="btn secondary" onclick="document.querySelector('#tips').style.display = ''">Tips for navigating in XR</button>
|
||||||
</td>
|
</div>
|
||||||
<td>
|
|
||||||
walk around / use right-longpinch (or thumbstick) for local teleports<br/>
|
<div id="tips" style="display:none">
|
||||||
</td>
|
<br/>
|
||||||
</tr>
|
<table>
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td width="200">
|
||||||
<b>PhoneVR</b>
|
<b>AR/VR headset</b>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
use gaze or bluetooth/mouse to navigate
|
walk around / use right-longpinch (or thumbstick) for local teleports<br/>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
<b>PhoneAR</b>
|
<b>PhoneVR</b>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
walk around and tap screen to click
|
use gaze or bluetooth/mouse to navigate
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
<b>Mobile</b>
|
<b>PhoneAR</b>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
use the WASD-keys and mousedrag to navigate
|
walk around and tap screen to click
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
<b>Keyboard+Mouse</b>
|
<b>Mobile</b>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
use the WASD-keys and mousedrag to navigate
|
use the WASD-keys and mousedrag to navigate
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
<tr>
|
||||||
|
<td>
|
||||||
|
<b>Keyboard+Mouse</b>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
use the WASD-keys and mousedrag to navigate
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
<br/>
|
<br/>
|
||||||
You've got this!
|
You've got this!
|
||||||
|
</div>
|
||||||
<br/>
|
<br/>
|
||||||
]],
|
]],
|
||||||
control='textarea',
|
control='textarea',
|
||||||
|
|
|
||||||
|
|
@ -27,11 +27,12 @@ local grid = {
|
||||||
local index = 0
|
local index = 0
|
||||||
for i, v in pairs(items) do
|
for i, v in pairs(items) do
|
||||||
local url = v.redirect and v.redirect or v.url
|
local url = v.redirect and v.redirect or v.url
|
||||||
|
local overlay = v.title:match('#overlay') and true or false
|
||||||
-- Position current item
|
-- Position current item
|
||||||
local pos = {
|
local pos = {
|
||||||
x = v.title:match("#overlay") and 0 or x * gridsize,
|
x = overlay and 0 or x * gridsize,
|
||||||
y = 0,
|
y = 0,
|
||||||
z = v.title:match("#overlay") and 0 or z * gridsize
|
z = overlay and 0 or z * gridsize
|
||||||
}
|
}
|
||||||
local posStr = pos.x .. ' ' .. pos.y .. ' ' .. pos.z
|
local posStr = pos.x .. ' ' .. pos.y .. ' ' .. pos.z
|
||||||
local is_nested_room = false
|
local is_nested_room = false
|
||||||
|
|
@ -41,24 +42,26 @@ local grid = {
|
||||||
else
|
else
|
||||||
jml = jml .. ' <object id="' .. url .. '" pos="' .. posStr .. '"/>\n'
|
jml = jml .. ' <object id="' .. url .. '" pos="' .. posStr .. '"/>\n'
|
||||||
end
|
end
|
||||||
index = index + 1
|
if not overlay then
|
||||||
-- Move to next position in the spiral
|
index = index + 1
|
||||||
if index < #items then
|
-- Move to next position in the spiral
|
||||||
x = x + dx
|
if index < #items then
|
||||||
z = z + dz
|
x = x + dx
|
||||||
stepsTaken = stepsTaken + 1
|
z = z + dz
|
||||||
if stepsTaken == steps then
|
stepsTaken = stepsTaken + 1
|
||||||
stepsTaken = 0
|
if stepsTaken == steps then
|
||||||
-- Rotate clockwise: right -> up -> left -> down
|
stepsTaken = 0
|
||||||
local oldDx = dx
|
-- Rotate clockwise: right -> up -> left -> down
|
||||||
dx = -dz
|
local oldDx = dx
|
||||||
dz = oldDx
|
dx = -dz
|
||||||
turns = turns + 1
|
dz = oldDx
|
||||||
-- Increase the length after every second turn
|
turns = turns + 1
|
||||||
if turns % 2 == 0 then
|
-- Increase the length after every second turn
|
||||||
steps = steps + 1
|
if turns % 2 == 0 then
|
||||||
end
|
steps = steps + 1
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return jml
|
return jml
|
||||||
|
|
|
||||||
|
|
@ -1,64 +1,312 @@
|
||||||
local util = require('util')
|
local util = require('util')
|
||||||
|
local apiSync = require('apiSync')
|
||||||
|
|
||||||
app.on('scan_experience', function(exp)
|
--app.on('init', function()
|
||||||
--print_r(exp)
|
-- app.plugin.manyfold.api = apiSync.init( 'manyfold' )
|
||||||
end)
|
--end)
|
||||||
|
|
||||||
app.on('scan_experience_file', function(exp)
|
local function scanTiltFile(exp)
|
||||||
end)
|
if exp.path:lower():match(".tilt$") then
|
||||||
|
app.markViewable(exp)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function scanForOpenBrush(exp)
|
||||||
|
local tags = app.plugin.manyfold.tags
|
||||||
|
if not tags then
|
||||||
|
tags = util.split( app.opts.plugin_manyfold_sync_hashtag, ' ')
|
||||||
|
table.insert( tags,"manyfold")
|
||||||
|
app.plugin.manyfold.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_manyfold_wrap_room
|
||||||
|
exp.parent.portal_width = tonumber( app.opts.plugin_manyfold_portal_width )
|
||||||
|
exp.parent.layout = app.opts.plugin_manyfold_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)
|
app.on('set', function(k,v)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
local sync_URL = app.opts_plugin_manyfold_sync_URL or ''
|
||||||
|
|
||||||
-- this registers the plugin UI
|
-- this registers the plugin UI
|
||||||
local plugin = {
|
local plugin = {
|
||||||
name = "Manyfold",
|
name = "ManyFold",
|
||||||
tab = "<img src='img/link.svg' class='icon invert'/> Manyfold",
|
infowide = true,
|
||||||
info = '',
|
info = [[
|
||||||
|
<img src="img/manyfold.png" style="width:50%; max-width:233px; display:inline-block; float:left; margin-right:20px;"/>
|
||||||
|
<a href="https://manyfold.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 or a grid.<br/>
|
||||||
|
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 manyfold.
|
||||||
|
]],
|
||||||
|
--buttons = {
|
||||||
|
-- { label = "resync now!", href="/reset" }
|
||||||
|
--},
|
||||||
opts = {
|
opts = {
|
||||||
{
|
{
|
||||||
key='app.opts.plugin_manyfold_instance_1',
|
key='app.opts.plugin_manyfold_wrap_room',
|
||||||
title='Instance 1 URL',
|
title='Wrap VR space',
|
||||||
info='<a href="https://manyfold.app/" target="_blank">Manyfold</a> URL goes here (https://xrforge.isvery.ninja e.g.)',
|
info='render 3D files in <a href="https://coderofsalvation.github.io/janus-guide/#/build/room" target="_blank">janusxr.xml</a> room',
|
||||||
default="https://xrforge.isvery.ninja",
|
default="room_plane",
|
||||||
values=''
|
values= app.plugin.janusxr.opts[4].values -- reuse rooms from janusxr plugin
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key='app.opts.plugin_manyfold_instance_2',
|
key='app.opts.plugin_manyfold_layout',
|
||||||
title='Instance 2 URL',
|
title='Layout assets',
|
||||||
info='<a href="https://manyfold.app/" target="_blank">Manyfold</a> URL goes here (https://xrforge.isvery.ninja e.g.)',
|
info='NOTE: use #overlay in asset-title to skip layout (sky/floor/e.g.)',
|
||||||
default="",
|
default="grid",
|
||||||
values=''
|
values={portals="As portals", grid="As grid"}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key='app.opts.plugin_manyfold_instance_3',
|
key='app.opts.plugin_manyfold_layout_grid_size',
|
||||||
title='Instance 3 URL',
|
title='Grid size',
|
||||||
info='<a href="https://manyfold.app/" target="_blank">Manyfold</a> URL goes here (https://xrforge.isvery.ninja e.g.)',
|
info='in square meters (only used for grid layout)',
|
||||||
default="",
|
default="3",
|
||||||
|
placeholder="3",
|
||||||
values=''
|
values=''
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key='app.opts.plugin_manyfold_sync_strategy',
|
key='app.opts.plugin_manyfold_sync_strategy',
|
||||||
title='Sync strategy',
|
title='How to sync',
|
||||||
info="Specify how content from manyfold instances should be fetched",
|
info='download assets locally (prevent linkrot)',
|
||||||
default="recent",
|
default = "mirror",
|
||||||
values={recent='only recent',all_recent='everything + recent', disabled='disabled'}
|
values={mirror="mirror locally"}
|
||||||
},
|
},
|
||||||
--{
|
{
|
||||||
-- key='app.opts.plugin_manyfold_sync_strategy',
|
key='app.opts.plugin_manyfold_sync_when',
|
||||||
-- title='Instance 3 URL',
|
title='Sync interval',
|
||||||
-- info='Specify how content from manyfold instances should be fetched",
|
info='when to sync?',
|
||||||
-- default="recent",
|
default = "boot",
|
||||||
-- values={recent='only recent',all_recent='everything + recent', disabled='disabled'}
|
values={disabled="disabled", boot="on boot", hourly="hourly", daily="daily"}
|
||||||
--},
|
},
|
||||||
--{
|
{
|
||||||
-- key='app.opts.plugin_manyfold_sync_interval',
|
key='app.opts.plugin_manyfold_sync_folder',
|
||||||
-- title='Instance 3 URL',
|
title='Sync to folder',
|
||||||
-- info='Specify how often xrforge should check for recent items #serveronly'
|
info='manyfold (icosa.gallery) assets will be saved here',
|
||||||
-- default="recent",
|
placeholder = "manyfold",
|
||||||
-- values={recent='only recent',all_recent='everything + recent', disabled='disabled'}
|
default = app.opts.plugin_diskscan_dir,
|
||||||
--},
|
values = {}
|
||||||
}
|
},
|
||||||
|
{
|
||||||
|
key='app.opts.plugin_manyfold_sync_folder_thumbnail',
|
||||||
|
title='Thumbnail',
|
||||||
|
info='URL of Thumbnail for folder/portal',
|
||||||
|
placeholder = "img/manyfold.png",
|
||||||
|
default = "img/manyfold.png",
|
||||||
|
values=''
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key='app.opts.plugin_manyfold_portal_width',
|
||||||
|
title='Portal spacing',
|
||||||
|
info='Spacing between portals (prevent thumbnail-overlap)',
|
||||||
|
placeholder = "4.2",
|
||||||
|
default = "4.2",
|
||||||
|
values=''
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key='app.opts.plugin_manyfold_sync_URL',
|
||||||
|
title='Icosa gallery URL',
|
||||||
|
info='public/selfhosted <a href="https://github.com/icosa-foundation/icosa-gallery target="_blank">icosa.gallery</a> server-instance',
|
||||||
|
placeholder = "icosa.gallery",
|
||||||
|
default = "icosa.gallery",
|
||||||
|
values=''
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key='app.opts.plugin_manyfold_sync_hashtag',
|
||||||
|
title='tags',
|
||||||
|
info=( sync_URL and 'space-separated tags will generate their own folders' or ''),
|
||||||
|
default = "#1 #xrforge",
|
||||||
|
placeholder = "#1 #xrforge",
|
||||||
|
values=''
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key='app.opts.plugin_manyfold_sync_category',
|
||||||
|
title='category',
|
||||||
|
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 = "ART",
|
||||||
|
default = "",
|
||||||
|
values=''
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key='app.opts.plugin_manyfold_sync_keywords',
|
||||||
|
title='keywords',
|
||||||
|
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_manyfold_sync_triangle_max',
|
||||||
|
title='Polygons maximum',
|
||||||
|
info=( sync_URL and 'performance-cap, leave empty or see <a href="https://api.' .. sync_URL .. '/v1/docs#/Assets/icosa_api_assets_get_assets">API docs</a>' or ''),
|
||||||
|
placeholder = "10000",
|
||||||
|
default = "",
|
||||||
|
values=''
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key='app.opts.plugin_manyfold_sync_license',
|
||||||
|
title='License',
|
||||||
|
info='filter assets on specific license',
|
||||||
|
default = "",
|
||||||
|
values={ [""]="", ALL_CC="CreateCommons (ALL)"}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key='app.opts.plugin_manyfold_sync_author',
|
||||||
|
title='Author name',
|
||||||
|
info='space-separated: filter assets on specific Author name',
|
||||||
|
placeholder='sarah',
|
||||||
|
default = "",
|
||||||
|
values=""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key='app.opts.plugin_manyfold_sync_authorid',
|
||||||
|
title='Author id',
|
||||||
|
info='space-separated: filter assets on specific Author id',
|
||||||
|
placeholder='9e8b98fbe',
|
||||||
|
default = "",
|
||||||
|
values=""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key='app.opts.plugin_manyfold_sync_maxitems',
|
||||||
|
title='Max latest items',
|
||||||
|
info='limit the total amount of result',
|
||||||
|
placeholder='3',
|
||||||
|
default = "3",
|
||||||
|
values=""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key='app.opts.plugin_manyfold_sync_collection',
|
||||||
|
title='In collection',
|
||||||
|
info='should assets be part of a collection?',
|
||||||
|
default = "disabled",
|
||||||
|
values={enabled="yes", disabled="no"}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
-- expected by src/.lua/apiSync.lua
|
||||||
|
apiCall = function(tag)
|
||||||
|
return 'https://api.' .. app.opts.plugin_manyfold_sync_URL .. '/v1/assets?xrforge=' .. EscapeFragment(app.host)
|
||||||
|
.. '&xrforge_strategy=' .. EscapeFragment(app.opts.plugin_manyfold_sync_strategy)
|
||||||
|
.. '&time=' .. tostring( os.time() )
|
||||||
|
.. (tag and #tag > 0 and '&keywords=' .. EscapeFragment(tag) or '')
|
||||||
|
.. (#app.opts.plugin_manyfold_sync_category > 0 and '&category=' .. EscapeFragment(app.opts.plugin_manyfold_sync_category) or '')
|
||||||
|
.. (#app.opts.plugin_manyfold_sync_keywords > 0 and '&keywords=' .. EscapeFragment(app.opts.plugin_manyfold_sync_keywords) or '')
|
||||||
|
.. (#app.opts.plugin_manyfold_sync_triangle_max > 0 and '&triangleCountMax=' .. EscapeFragment(app.opts.plugin_manyfold_sync_triangle_max) or '')
|
||||||
|
.. (#app.opts.plugin_manyfold_sync_license > 0 and '&license=' .. app.opts.plugin_manyfold_sync_license or '' )
|
||||||
|
.. (app.opts.plugin_manyfold_sync_collection == 'enabled' and '&inCollection=true' or '' )
|
||||||
|
.. (#app.opts.plugin_manyfold_sync_maxitems > 0 and '&pageSize=' .. EscapeFragment(app.opts.plugin_manyfold_sync_maxitems) or '' )
|
||||||
|
-- TODO: filter AFTER the request
|
||||||
|
-- .. (#app.opts.plugin_manyfold_sync_author > 0 and '&authorName=' .. EscapeFragment(app.opts.plugin_manyfold_sync_author) or '')
|
||||||
|
-- .. (#app.opts.plugin_manyfold_sync_authorid > 0 and '&authorId=' .. EscapeFragment(app.opts.plugin_manyfold_sync_authorid) or '')
|
||||||
|
end,
|
||||||
|
|
||||||
|
getFolder = function(tag)
|
||||||
|
local folder = app.opts.plugin_manyfold_sync_folder
|
||||||
|
if tag then
|
||||||
|
folder = folder .. '/' .. util.hyphenate(tag)
|
||||||
|
end
|
||||||
|
return folder
|
||||||
|
end,
|
||||||
|
|
||||||
|
-- expected by src/.lua/apiSync.lua
|
||||||
|
fetch = function(url,opts)
|
||||||
|
opts = opts or {}
|
||||||
|
if not opts.body then
|
||||||
|
local ok, headers, body = Fetch( url )
|
||||||
|
if not ok then
|
||||||
|
return print("[manyfold.lua] error: could not fetch: " .. app.plugin.manyfold.apiCall(tag))
|
||||||
|
end
|
||||||
|
opts.body = body
|
||||||
|
end
|
||||||
|
return DecodeJson(opts.body)
|
||||||
|
end,
|
||||||
|
|
||||||
|
-- expected by src/.lua/apiSync.lua
|
||||||
|
download = function(t,tag)
|
||||||
|
local me = app.plugin.manyfold
|
||||||
|
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 then
|
||||||
|
print('✅ ' .. asset.displayName .. ' (syncing) #manyfold')
|
||||||
|
util.writefile( jsonfile, EncodeJson(asset) )
|
||||||
|
local rootfile = dirnameFull .. '/' .. f.root.url:gsub(".*/","")
|
||||||
|
local thumbnailfile = ''
|
||||||
|
if asset.thumbnail then
|
||||||
|
thumbnailfile = dirnameFull .. '.' .. asset.thumbnail.url:gsub(".*%.",'')
|
||||||
|
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('ℹ️ ' .. asset.displayName .. ' (up to date) #manyfold')
|
||||||
|
end
|
||||||
|
else
|
||||||
|
print('[manyfold.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
|
||||||
|
|
|
||||||
|
|
@ -1,34 +1,8 @@
|
||||||
local util = require('util')
|
local util = require('util')
|
||||||
local apiSync = require('apiSync')
|
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()
|
||||||
foreach( app.plugin.openbrush.opts, function(k,opt)
|
app.plugin.openbrush.api = apiSync.init( 'openbrush' )
|
||||||
if opt.key == 'app.opts.plugin_openbrush_sync_folder' then
|
|
||||||
opt.values = app.cache.dirs
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
if util.contains(argv, 'generate') then
|
|
||||||
syncIcosaGallery()
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
|
|
||||||
app.on('scheduler', function(opts)
|
|
||||||
if util.contains(argv, 'server') then
|
|
||||||
if util.cmdExist('wget') and opts.interval == app.opts.plugin_openbrush_sync_when then
|
|
||||||
syncIcosaGallery()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end)
|
end)
|
||||||
|
|
||||||
local function scanTiltFile(exp)
|
local function scanTiltFile(exp)
|
||||||
|
|
@ -52,6 +26,7 @@ local function scanForOpenBrush(exp)
|
||||||
end
|
end
|
||||||
exp.parent.janusxr.room = app.opts.plugin_openbrush_wrap_room
|
exp.parent.janusxr.room = app.opts.plugin_openbrush_wrap_room
|
||||||
exp.parent.portal_width = tonumber( app.opts.plugin_openbrush_portal_width )
|
exp.parent.portal_width = tonumber( app.opts.plugin_openbrush_portal_width )
|
||||||
|
exp.parent.grid_size = tonumber( app.opts.plugin_openbrush_grid_size )
|
||||||
exp.parent.layout = app.opts.plugin_openbrush_layout
|
exp.parent.layout = app.opts.plugin_openbrush_layout
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
@ -75,39 +50,42 @@ local plugin = {
|
||||||
info = [[
|
info = [[
|
||||||
<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 or a grid.<br/>
|
||||||
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>
|
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.
|
<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.
|
||||||
]],
|
]],
|
||||||
|
--buttons = {
|
||||||
|
-- { label = "resync now!", href="/reset" }
|
||||||
|
--},
|
||||||
opts = {
|
opts = {
|
||||||
{
|
{
|
||||||
key='app.opts.plugin_openbrush_wrap_room',
|
key='app.opts.plugin_openbrush_wrap_room',
|
||||||
title='Wrap VR space',
|
title='Wrap VR space',
|
||||||
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="room_plane",
|
default="",
|
||||||
values= app.plugin.janusxr.opts[4].values -- reuse rooms from janusxr plugin
|
values= app.plugin.janusxr.opts[4].values -- reuse rooms from janusxr plugin
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key='app.opts.plugin_openbrush_layout',
|
key='app.opts.plugin_openbrush_layout',
|
||||||
title='Layout assets',
|
title='Layout assets',
|
||||||
info='NOTE: use #overlay in asset-title to skip layout (sky/floor/e.g.)',
|
info='NOTE: use #overlay in asset-title to skip layout (sky/floor/e.g.)',
|
||||||
default="portals",
|
default="grid",
|
||||||
values={portals="As portals", grid="As grid"}
|
values={portals="As portals", grid="As grid"}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key='app.opts.plugin_openbrush_layout_grid_size',
|
key='app.opts.plugin_openbrush_layout_grid_size',
|
||||||
title='Grid size',
|
title='Grid size',
|
||||||
info='in square meters (unused for non-grid layout)',
|
info='in square meters (only used for grid layout)',
|
||||||
default="3",
|
default="75",
|
||||||
placeholder="3",
|
placeholder="75",
|
||||||
values=''
|
values=''
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key='app.opts.plugin_openbrush_sync_strategy',
|
key='app.opts.plugin_openbrush_sync_strategy',
|
||||||
title='How to sync',
|
title='How to sync',
|
||||||
info='mirroring = most resilient; references = instant updates',
|
info='download assets locally (prevent linkrot)',
|
||||||
default = "mirror",
|
default = "mirror",
|
||||||
values={mirror="mirror locally",reference="reference remote URL"}
|
values={mirror="mirror locally"}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key='app.opts.plugin_openbrush_sync_when',
|
key='app.opts.plugin_openbrush_sync_when',
|
||||||
|
|
@ -116,13 +94,6 @@ local plugin = {
|
||||||
default = "boot",
|
default = "boot",
|
||||||
values={disabled="disabled", boot="on boot", hourly="hourly", daily="daily"}
|
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',
|
title='Sync to folder',
|
||||||
|
|
@ -142,7 +113,7 @@ local plugin = {
|
||||||
{
|
{
|
||||||
key='app.opts.plugin_openbrush_portal_width',
|
key='app.opts.plugin_openbrush_portal_width',
|
||||||
title='Portal spacing',
|
title='Portal spacing',
|
||||||
info='Adjust spacing between portals (prevent thumbnail-overlap)',
|
info='Spacing between portals (prevent thumbnail-overlap)',
|
||||||
placeholder = "4.2",
|
placeholder = "4.2",
|
||||||
default = "4.2",
|
default = "4.2",
|
||||||
values=''
|
values=''
|
||||||
|
|
@ -159,8 +130,8 @@ local plugin = {
|
||||||
key='app.opts.plugin_openbrush_sync_hashtag',
|
key='app.opts.plugin_openbrush_sync_hashtag',
|
||||||
title='tags',
|
title='tags',
|
||||||
info=( sync_URL and 'space-separated tags will generate their own folders' or ''),
|
info=( sync_URL and 'space-separated tags will generate their own folders' or ''),
|
||||||
default = "#1 #xrforge",
|
default = "#xrforge",
|
||||||
placeholder = "#1 #xrforge",
|
placeholder = "#xrforge",
|
||||||
values=''
|
values=''
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -187,6 +158,13 @@ local plugin = {
|
||||||
default = "",
|
default = "",
|
||||||
values=''
|
values=''
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
key='app.opts.plugin_openbrush_sync_license',
|
||||||
|
title='License',
|
||||||
|
info='filter assets on specific license',
|
||||||
|
default = "",
|
||||||
|
values={ [""]="", ALL_CC="CreateCommons (ALL)"}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
key='app.opts.plugin_openbrush_sync_author',
|
key='app.opts.plugin_openbrush_sync_author',
|
||||||
title='Author name',
|
title='Author name',
|
||||||
|
|
@ -220,14 +198,16 @@ local plugin = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
-- expected by src/.lua/apiSync.lua
|
||||||
apiCall = function(tag)
|
apiCall = function(tag)
|
||||||
return 'https://api.' .. app.opts.plugin_openbrush_sync_URL .. '/v1/assets?xrforge=' .. EscapeFragment(app.host)
|
return 'https://api.' .. app.opts.plugin_openbrush_sync_URL .. '/v1/assets?xrforge=' .. EscapeFragment(app.host)
|
||||||
.. '&xrforge_strategy=' .. EscapeFragment(app.opts.plugin_openbrush_sync_strategy)
|
.. '&xrforge_strategy=' .. EscapeFragment(app.opts.plugin_openbrush_sync_strategy)
|
||||||
|
.. '&time=' .. tostring( os.time() )
|
||||||
.. (tag and #tag > 0 and '&keywords=' .. EscapeFragment(tag) or '')
|
.. (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_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_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_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_license > 0 and '&license=' .. app.opts.plugin_openbrush_sync_license or '' )
|
||||||
.. (app.opts.plugin_openbrush_sync_collection == 'enabled' and '&inCollection=true' 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 '' )
|
.. (#app.opts.plugin_openbrush_sync_maxitems > 0 and '&pageSize=' .. EscapeFragment(app.opts.plugin_openbrush_sync_maxitems) or '' )
|
||||||
-- TODO: filter AFTER the request
|
-- TODO: filter AFTER the request
|
||||||
|
|
@ -243,6 +223,7 @@ local plugin = {
|
||||||
return folder
|
return folder
|
||||||
end,
|
end,
|
||||||
|
|
||||||
|
-- expected by src/.lua/apiSync.lua
|
||||||
fetch = function(url,opts)
|
fetch = function(url,opts)
|
||||||
opts = opts or {}
|
opts = opts or {}
|
||||||
if not opts.body then
|
if not opts.body then
|
||||||
|
|
@ -255,6 +236,7 @@ local plugin = {
|
||||||
return DecodeJson(opts.body)
|
return DecodeJson(opts.body)
|
||||||
end,
|
end,
|
||||||
|
|
||||||
|
-- expected by src/.lua/apiSync.lua
|
||||||
download = function(t,tag)
|
download = function(t,tag)
|
||||||
local me = app.plugin.openbrush
|
local me = app.plugin.openbrush
|
||||||
local synced = 0
|
local synced = 0
|
||||||
|
|
@ -292,11 +274,13 @@ local plugin = {
|
||||||
sync = false -- already finished
|
sync = false -- already finished
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if sync or app.opts.plugin_openbrush_sync_bruteforce == 'enabled' then
|
if sync then
|
||||||
|
print('✅ ' .. asset.displayName .. ' (syncing) #openbrush')
|
||||||
util.writefile( jsonfile, EncodeJson(asset) )
|
util.writefile( jsonfile, EncodeJson(asset) )
|
||||||
local rootfile = dirnameFull .. '/' .. f.root.url:gsub(".*/","")
|
local rootfile = dirnameFull .. '/' .. f.root.url:gsub(".*/","")
|
||||||
local thumbnailfile = dirnameFull .. '.' .. asset.thumbnail.url:gsub(".*%.",'')
|
local thumbnailfile = ''
|
||||||
if app.opts.plugin_openbrush_layout == 'portals' then
|
if asset.thumbnail and asset.thumbnail.url then
|
||||||
|
thumbnailfile = dirnameFull .. '.' .. asset.thumbnail.url:gsub(".*%.",'')
|
||||||
print('🤞 fetch ' .. asset.thumbnail.url)
|
print('🤞 fetch ' .. asset.thumbnail.url)
|
||||||
os.execute('wget "' ..asset.thumbnail.url .. '" -O "' .. thumbnailfile .. '"')
|
os.execute('wget "' ..asset.thumbnail.url .. '" -O "' .. thumbnailfile .. '"')
|
||||||
end
|
end
|
||||||
|
|
@ -311,7 +295,7 @@ local plugin = {
|
||||||
end
|
end
|
||||||
synced = synced + 1
|
synced = synced + 1
|
||||||
else
|
else
|
||||||
print('[openbrush.lua] ' .. asset.displayName .. ' is up to date')
|
print('ℹ️ ' .. asset.displayName .. ' (up to date) #openbrush')
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
print('[openbrush.lua] error: no suitable format found for ' .. asset.displayName .. ' ' .. asset.assetId)
|
print('[openbrush.lua] error: no suitable format found for ' .. asset.displayName .. ' ' .. asset.assetId)
|
||||||
|
|
|
||||||
|
|
@ -47,6 +47,8 @@ return {
|
||||||
if pid ~= nil then
|
if pid ~= nil then
|
||||||
unix.kill( pid, unix.SIGTERM)
|
unix.kill( pid, unix.SIGTERM)
|
||||||
end
|
end
|
||||||
|
print("gracefully stopping child-processes")
|
||||||
|
unix.wait()
|
||||||
unix.exit()
|
unix.exit()
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue