Compare commits

...

3 commits

6 changed files with 177 additions and 42 deletions

View file

@ -127,7 +127,8 @@ set_upload_path(){
test -d $UPLOAD_PATH || mkdir $UPLOAD_PATH test -d $UPLOAD_PATH || mkdir $UPLOAD_PATH
add_lib_to_db $UPLOAD_PATH add_lib_to_db $UPLOAD_PATH
id=$(sqlite3 $db "select id from libraries where path = '$UPLOAD_PATH';") id=$(sqlite3 $db "select id from libraries where path = '$UPLOAD_PATH';")
sqlite3 $db "INSERT INTO settings VALUES(6,'default_library',replace('--- $id\n','\n',char(10); set_global model_path_template "replace('--- \"{creator}/{modelId} \"\\n','\\n',char(10))"
sqlite3 $db "INSERT INTO settings VALUES(6,'default_library',$id);
INSERT INTO sqlite_sequence VALUES('settings',6);" INSERT INTO sqlite_sequence VALUES('settings',6);"
} }
@ -258,8 +259,8 @@ init_database(){
set_global site_tagline "'$TAGLINE'" set_global site_tagline "'$TAGLINE'"
set_global model_tags_auto_tag_new "replace('--- \"\"\\n','\\n',char(10))" set_global model_tags_auto_tag_new "replace('--- \"\"\\n','\\n',char(10))"
set_global model_path_template "replace('--- \"{creator}/{modelId} \"\\n','\\n',char(10))" set_global model_path_template "replace('--- \"{creator}/{modelId} \"\\n','\\n',char(10))"
set_upload_path
#set_global about "$ABOUT" #set_global about "$ABOUT"
set_upload_path &
get_xrfragment_assets get_xrfragment_assets
mount_dir mount_dir
BOOT_SCAN=1 scan_libraries & BOOT_SCAN=1 scan_libraries &

View file

@ -10,6 +10,7 @@ if ARGV.length != 1
end end
filename = ARGV[0] filename = ARGV[0]
JMLHeuristic = /<fireboxroom>.*?<\/fireboxroom>/im
begin begin
@ -66,20 +67,44 @@ begin
# Get the value of the environment variable FEDERATE_DRIVE_HOST # Get the value of the environment variable FEDERATE_DRIVE_HOST
federate_drive_host = ENV['FEDERATE_DRIVE_HOST'] federate_drive_host = ENV['FEDERATE_DRIVE_HOST']
# Define the HTML content using a multi-line string (heredoc) autogenerate = true
# Ruby's heredoc allows for variable interpolation (using #{})
jml = <<~JML if data['description'] && data['description'].match(JMLHeuristic)
<FireBoxRoom> if data['description'].match(/autogenerate=['"]false['"]/)
<Assets> XRForge.log("✅ autogenerate='false' found in JML..keeping this JML")
<assetobject id="experience" src="#{federate_drive_host}/#{model_file.gsub("#","%23")}"/> autogenerate = false
</Assets> jmlMatch = data['description'].match(JMLHeuristic)
<Room pbr="true" #{ data['keywords'].include?('singleuser') ? "private='true'" : ""}> jml = jmlMatch[0]
<object pos="0 0 0" collision_id="experience" id="experience" /> end
</Room> end
</FireBoxRoom> if autogenerate
<!-- archive.org hints --> jml = <<~JML
<a href="#{federate_drive_host}/#{model_file.gsub("#","%23")}"></a> <FireBoxRoom>
JML <Assets>
<assetobject id="experience" src="#{federate_drive_host}/#{model_file.gsub("#","%23")}"/>
</Assets>
<Room autogenerate="true" #{ data['keywords'].include?('singleuser') ? "private='true'" : ""}>
<object pos="0 0 0" collision_id="experience" id="experience" />
</Room>
</FireBoxRoom>
<!-- archive.org hints -->
<a href="#{federate_drive_host}/#{model_file.gsub("#","%23")}"></a>
JML
data['description'] = data['description'] ? data['description'] : "your description here\n"
data['description'] = <<~DESCRIPTION#{data['description']}
<!-- Hi there! Below is autogenerated JanusXR Markup (JML). -->
<!-- If you want to tweak it, then first disable autogeneration -->
<!-- How? make sure to set autogenerate="false" (see room-tag below) -->
<!-- -->
<!-- JML info: -->
<!-- https://coderofsalvation.github.io/janus-guide/#/examples/markup -->
<!-- https://janusxr.org/docs/build/introtojml/index.html -->
#{jml}
DESCRIPTION
File.write("datapackage.json", JSON.pretty_generate(data) )
end
html = <<~HTML html = <<~HTML
<!DOCTYPE html> <!DOCTYPE html>
@ -92,6 +117,8 @@ begin
<janus-viewer> <janus-viewer>
#{jml} #{jml}
</janus-viewer> </janus-viewer>
<!-- archive.org hints -->
<a href="#{federate_drive_host}/#{model_file.gsub("#","%23")}"></a>
</body> </body>
</html> </html>
HTML HTML
@ -116,3 +143,4 @@ rescue JSON::ParserError
rescue => e rescue => e
puts "An error occurred: #{e.message}" puts "An error occurred: #{e.message}"
end end

View file

@ -19,28 +19,11 @@ if ! filename.end_with?("datapackage.json")
exit 0 exit 0
end end
begin def generate(aphandle,filename,dir,data,logfile)
# Change the directory
dir = File.dirname(filename)
Dir.chdir( dir )
# Read and parse the JSON file
data = JSON.parse( File.read( "datapackage.json" ) )
APHandle = false
data['keywords'].any? do |tag| XRForge.log("✅ starting mastodon-post for #{aphandle}", logfile)
if tag.match?(/@.*@.*\./) # scan for activitypub handle (@foo@mastodon.online e.g.)
APHandle = tag
end
end
if ! APHandle # nothing to do parts = aphandle.split("@")
exit 0
end
logfile = File.join( File.dirname(filename), ".xrforge/log.txt" )
XRForge.log("✅ starting Mastodon post2image", logfile)
parts = APHandle.split("@")
server = parts[2].sub(/@/,"") server = parts[2].sub(/@/,"")
rssUrl = "https://#{server}/@#{parts[1]}.rss" rssUrl = "https://#{server}/@#{parts[1]}.rss"
XRForge.log("✅ checking #{rssUrl}", logfile) XRForge.log("✅ checking #{rssUrl}", logfile)
@ -53,7 +36,8 @@ begin
first_item = feed.items.first first_item = feed.items.first
if first_item if first_item
description = CGI.unescapeHTML(first_item.description.to_s) description = CGI.unescapeHTML(first_item.description.to_s)
.gsub(/<[^>]*>/, '') # remove other tags .split("\n")[0,4].join("\n") # max 5 lines
.gsub(/<[^>]*>/, '') # remove other tags
#.gsub(/<br\s*\/?>/i, "\n") # preserve linebreaks #.gsub(/<br\s*\/?>/i, "\n") # preserve linebreaks
#.gsub(/<\/p>/i, "\n") # preserve linebreaks #.gsub(/<\/p>/i, "\n") # preserve linebreaks
description = description.length > 130 ? description = description[0,130] + " (..)" : description description = description.length > 130 ? description = description[0,130] + " (..)" : description
@ -65,14 +49,29 @@ begin
puts description puts description
# look for first image # look for first image
MEDIA_REGEX = /<media:content url=['"]([^'"]+)['"]/ media_regex = /<media:content url=['"]([^'"]+)['"]/
img = feed.to_s.match(MEDIA_REGEX) img = feed.to_s.match(media_regex)
if img and img[1] and img[1].match(/(png|jpg)/) if img and img[1] and img[1].match(/(png|jpg)/)
imgurl = img[1] imgurl = img[1]
end end
# generate the final .glb # generate the final .glb
system("/root/templates/mastodon-post/mastodon-post.sh", description, feed.channel.title, feed.channel.link, APHandle, "#{dir}/mastodon-post.glb") system("/root/templates/mastodon-post/mastodon-post.sh", description, feed.channel.title, feed.channel.link, aphandle, "#{dir}/#{aphandle}.glb")
end
begin
# Change the directory
dir = File.dirname(filename)
Dir.chdir( dir )
# Read and parse the JSON file
data = JSON.parse( File.read( "datapackage.json" ) )
logfile = File.join( File.dirname(filename), ".xrforge/log.txt" )
data['keywords'].any? do |tag|
if tag.match?(/@.*@.*\./) # scan for activitypub handle (@foo@mastodon.online e.g.)
generate(tag,filename,dir,data,logfile)
end
end
XRForge.log(" ", logfile) XRForge.log(" ", logfile)

View file

@ -1,6 +1,9 @@
require 'rexml/document'
require 'rexml/formatters/pretty'
module XRForge module XRForge
MODEL_EXT = ['.glb', '.gltf', '.blend', '.usdz', '.obj', '.dae'] MODEL_EXT = ['.glb', '.gltf', '.blend', '.usdz', '.obj', '.dae', '.x3d']
def self.log(message, filename) def self.log(message, filename)
# Append the log entry to the log file # Append the log entry to the log file
@ -10,4 +13,97 @@ module XRForge
puts("#{message}\n") puts("#{message}\n")
end end
# ==============================================================================
# 1. JSON (Ruby Hash) to XML String
# ==============================================================================
# Internal recursive helper for converting Hash/Array structure to REXML.
# Handles attribute conversion (@key -> attribute), hash nesting, and arrays
# (which create multiple sibling elements of the same name).
#
# @param data [Hash, Array, String] The data fragment.
# @param p [REXML::Element] The parent REXML element.
def self.j2x_rec(data, p)
# Check if data is a Hash (for attributes and children)
if data.is_a?(Hash)
data.each { |k, v| k.start_with?('@') ? p.attributes[k[1..-1]] = v.to_s : j2x_rec(v, p.add_element(k)) }
# Check if data is an Array (for repeated elements)
elsif data.is_a?(Array)
# The parent in this context (e.g., <obj>) is already created. For each array item,
# we need to create a new element with the same name as the current parent's *name*
# and attach it to the parent's *parent*.
# This requires looking up the parent's parent, which breaks terseness.
# We must use a simpler recursive structure for compactness.
# We assume array structure requires us to create a new element named by the
# parent, and assign attributes/children recursively.
data.each { |v| j2x_rec(v, p) } # Recursive call to continue processing children
# Handle text content
else
p.text = data.to_s
end
end
# The entry point for converting Ruby Hash to XML string.
#
# @param data [Hash] The root hash structure.
# @return [String] The resulting compact XML string.
def self.json2xml(data)
# Corrected recursive logic for arrays that are values (e.g., "obj" => [..])
j2x_map = lambda do |d, p_name, p|
if d.is_a?(Hash)
d.each { |k, v| k.start_with?('@') ? p.attributes[k[1..-1]] = v.to_s : j2x_map.call(v, k, p.add_element(k)) }
elsif d.is_a?(Array)
# If the value is an array, we iterate, create a sibling element for each, and recurse
d.each { |v| j2x_map.call(v, p.name, p.parent.add_element(p.name)) }
p.remove # Remove the placeholder element created before hitting the array
else
p.text = d.to_s
end
end
# Initialize Document and process the single root key
doc = REXML::Document.new
data.each { |k, v| j2x_map.call(v, k, doc.add_element(k)) }
# Output with 2-space indentation
o = ""; f = REXML::Formatters::Default.new; f.indentation = 2; f.write(doc.root, o); o.strip
end
# ==============================================================================
# 2. XML String to JSON (Ruby Hash)
# ==============================================================================
# Internal recursive helper for converting REXML Element to Hash structure.
# Handles attribute conversion (attribute -> @key), text, and array creation for siblings.
#
# @param e [REXML::Element] The XML element.
# @return [Hash, String] The resulting hash or simple text value.
def self.x2j_rec(e)
h = {}; e.attributes.each { |k, v| h["@#{k}"] = v } # Attributes first
e.elements.each do |c| # Iterate children
v = x2j_rec(c)
if h.key?(c.name)
h[c.name] = [h[c.name]] unless h[c.name].is_a?(Array)
h[c.name] << v
else
h[c.name] = v
end
end
# Check for text content if no children were processed
t = e.get_text.to_s.strip; return t if t && !t.empty? && h.empty?; return h
end
# The entry point for converting XML string to Ruby Hash.
#
# @param xmlstr [String] The XML string.
# @return [Hash] The resulting Ruby hash structure.
def self.xml2json(xmlstr)
doc = REXML::Document.new(xmlstr)
root = doc.root
{ root.name => x2j_rec(root) }
end
end end

View file

@ -8,7 +8,7 @@ echo "Preparing database..."
bundle exec rails db:prepare:with_data bundle exec rails db:prepare:with_data
echo "run boot 'hook'" echo "run boot 'hook'"
/manyfold/cli/manyfold.sh hook boot & /manyfold/cli/manyfold.sh hook boot || true
echo "Setting database file ownership (SQLite3 only)..." echo "Setting database file ownership (SQLite3 only)..."
bundle exec rake db:chown bundle exec rake db:chown

View file

@ -21,3 +21,14 @@ input[type="checkbox"]:checked + .hidden-tooltip {
display: block; display: block;
} }
#model_notes {
min-height: 50vh;
font-family: monospace;
font-size: 13px;
color: #CCF;
background: #000;
}
.col-form-label{
width:111px;
}