diff --git a/manyfold/root/.config/janus-server/config.js b/manyfold/root/.config/janus-server/config.js new file mode 100644 index 0000000..2bad509 --- /dev/null +++ b/manyfold/root/.config/janus-server/config.js @@ -0,0 +1,92 @@ +var fs = require('fs'); + +module.exports = { + /* Socket port to listen on */ + port: 5566, + + /* SSL configurations */ + + /* + ************************************************************************ + *** The following options REQUIRE a redis database to function ! *** + ************************************************************************ + */ + multiprocess: { + enabled: false, // requires redis for IPC + processes: 1 + }, + partyList: false, + redis: { + host: "127.0.0.1", + port: 6379, + //password: null + }, + + + /* + ************************************************************************ + *** The following options REQUIRE a MySQL database to function ! *** + ************************************************************************ + */ + + //// If you want to track how many users are online, set UserList: true. + //Userlist: false, + + ///* Controls how many results a request for 'users_online' receives. */ + //maxUserResults: 100, + + ///* MySQL database connection info for janus-mysql-auth and janus-mysql-userlist */ + //MySQL_Hostname: 'localhost', + //MySQL_Database: 'janusvr', + //MySQL_Username: 'janusvr', + //MySQL_Password: 'janusvr', + + ///* Authentication mode: + // 'none' - Will not attempt to authenticate users, + // anyone can connect with any unused userId. + // 'optional' - Anyone can connect, but if userId has been registered + // a password must be provided. + // 'required' - Only users with userids and passwords are allowed to connect. + //*/ + //authMode: "none", + // + //popularRooms: { + // halfLife: 7 * 24 * 60 * 60 * 1000, // set halflife to 7 days + // updateInterval: 3000, // interval between weight updates on the popular rooms + // masterToken: "changethis" + //}, + + /* + ************************************************************************ + *** The previous options REQUIRE a MySQL database to function ! *** + ************************************************************************ + */ + + + + /* Plugins must be installed from npm, or manually created in node_module/ to be loaded. */ + /* hookPlugins are called while parsing messages */ + hookPlugins: { + logon: { + plugins: [ + //" janus-mysql-auth" + ] + }, + enter_room: { + plugins: [ + // "janus-mysql-popular" + ] + } + + }, + + /* methodPlugins add new commands to the server */ + methodPlugins: { + // ping: { plugin: "janus-method-ping" } + }, + + /* intervalPlugins are called in intervals specified in seconds. */ + intervalPlugins: [ + //{ plugin: "janus-mysql-userlist-official", interval: 6 } + ], +}; diff --git a/manyfold/root/bin/random_thumbnail.sh b/manyfold/root/bin/random_thumbnail.sh new file mode 100755 index 0000000..7ac947b --- /dev/null +++ b/manyfold/root/bin/random_thumbnail.sh @@ -0,0 +1,76 @@ +#!/bin/sh + +test -n "$2" || { echo "Usage: $0 [blurtimes]"; exit 0; } + +# --- Configuration --- +# Define the size of the rectangle to crop (Width x Height) +CROP_SIZE="128x128" +CROP_WIDTH=128 +CROP_HEIGHT=128 +BLURTIMES=0 + +test -n "$3" && BLURTIMES=$3 + +# Input and Output filenames +INPUT_FILE="$1" # <-- **CHANGE THIS** to your actual input file name +OUTPUT_FILE="$2" + +# --- Pre-Check: Ensure the input file exists --- +if [ ! -f "$INPUT_FILE" ]; then + echo "🚨 Error: Input file '$INPUT_FILE' not found." + echo "Please update the INPUT_FILE variable in the script." + exit 1 +fi + +# --- 1. Get the dimensions of the input image --- +# The 'identify' command returns image info; we use awk/cut to extract just the dimensions. +# Example output format: "1920x1080" +IMAGE_GEOMETRY=$(identify -format "%wx%h" "$INPUT_FILE") +IMAGE_WIDTH=$(echo "$IMAGE_GEOMETRY" | cut -dx -f1) +IMAGE_HEIGHT=$(echo "$IMAGE_GEOMETRY" | cut -dx -f2) + +echo "Input Image: $INPUT_FILE (${IMAGE_GEOMETRY})" +echo "Crop Area: $CROP_SIZE" + +# --- 2. Calculate the maximum possible starting coordinates --- +# To ensure the 16x16 crop doesn't go off the edge: +# Max_X_Start = Image_Width - Crop_Width +# Max_Y_Start = Image_Height - Crop_Height +MAX_X=$((IMAGE_WIDTH - CROP_WIDTH)) +MAX_Y=$((IMAGE_HEIGHT - CROP_HEIGHT)) + +# Check if the image is too small to crop +if [ $MAX_X -lt 0 ] || [ $MAX_Y -lt 0 ]; then + echo "🚨 Error: Image dimensions ($IMAGE_GEOMETRY) are smaller than the crop size ($CROP_SIZE)." + exit 1 +fi + +# --- 3. Generate Random Coordinates --- +# $RANDOM generates a pseudo-random integer (0 to 32767). +# We use the modulo operator (%) to limit it to the required range (0 to MAX_X or MAX_Y). + +# Random X-coordinate (0 to MAX_X) +RANDOM_X=$((RANDOM % (MAX_X + 1))) + +# Random Y-coordinate (0 to MAX_Y) +RANDOM_Y=$((RANDOM % (MAX_Y + 1))) + +# --- 4. Assemble the ImageMagick Geometry String --- +# The final geometry string is: x++ +CROP_GEOMETRY="${CROP_SIZE}+${RANDOM_X}+${RANDOM_Y}" + +echo "Random Start Coordinate: X=${RANDOM_X}, Y=${RANDOM_Y}" +echo "ImageMagick Crop String: ${CROP_GEOMETRY}" + +# --- 5. Execute the ImageMagick Command --- +# 'convert' is the core command. +# -crop: Specifies the geometry string for cropping. +# -quality 100: (Optional) Ensures best quality for the output JPEG. +test -f $OUTPUT_FILE && rm $OUTPUT_FILE +magick "$INPUT_FILE" \ + -crop "$CROP_GEOMETRY" \ + -blur 0x$BLURTIMES \ + -quality 100 \ + "$OUTPUT_FILE" + +echo "✅ thumbnail generated" diff --git a/manyfold/root/hook.d/boot/import_instances.rb b/manyfold/root/hook.d/boot/import_instances.rb new file mode 100755 index 0000000..25ff3e3 --- /dev/null +++ b/manyfold/root/hook.d/boot/import_instances.rb @@ -0,0 +1,116 @@ +#!/usr/bin/env ruby +require 'yaml' # For reading input +require 'json' # <--- REQUIRED FOR OUTPUT +require 'fileutils' +require 'pathname' +require 'net/http' +require 'uri' +require 'erb' + +if ! ENV['IMPORT_INSTANCES'] + exit 0 # nothing to do +end + +INPUT_ROOT_DIR = '/root/instances' +OUTPUT_ROOT_DIR = '/mnt/instances' + +FileUtils.mkdir_p(OUTPUT_ROOT_DIR) + +puts "scanning yaml-files in /root/instances" + +# 2. Iterate over all 'room.yaml' files recursively +Dir.glob(File.join(INPUT_ROOT_DIR, '**', 'room.yaml')).each do |input_file_path| + + begin + input_data = YAML.load_file(input_file_path) + rescue StandardError => e + puts " ❌ Error reading or parsing YAML: #{e.message}" + next + end + + relative_path = Pathname.new(input_file_path).relative_path_from(Pathname.new(INPUT_ROOT_DIR)) + path_components = relative_path.to_s.split(File::SEPARATOR) + + unless path_components.length >= 4 + puts " ⚠️ Skipping: Path structure too shallow. Expected SERVER/AUTHOR/ROOM_NAME/room.yaml" + next + end + + server_name = path_components[0] + author_name = path_components[1] + room_name = path_components[2] + + begin + u = input_data['url'] + r = Net::HTTP.new(URI.parse(u).host, URI.parse(u).port) + r.use_ssl = (URI.parse(u).scheme == 'https') + + a = r.request(Net::HTTP::Head.new(URI.parse(u).request_uri)) + + if a.code.to_i != 200 + puts " ⚠️ Skipping:HTTP code #{a.code} received :/" + next + end + + # 5. Construct the final data structure (Hash) + output_data = {} + output_data['homepage'] = input_data['url'] + output_data['name'] = input_data['title'] + output_data['description'] = input_data['description'] + output_data['image'] = input_data['thumbnail'] + output_data['tags'] = [server_name] + + output_dir = File.join(OUTPUT_ROOT_DIR, author_name, room_name) + output_file_path = File.join(output_dir, 'datapackage.json') + + begin + + url = URI(input_data['thumbnail']) + output_path = output_dir+"/.xrforge/thumbnail.jpg" + + Net::HTTP.start(url.host, url.port, use_ssl: url.scheme == "https") do |http| + request = Net::HTTP::Get.new(url) + http.request(request) do |response| + raise "Download failed: #{response.code}" unless response.is_a?(Net::HTTPSuccess) + + File.open(output_path, "wb") do |file| + response.read_body do |chunk| + file.write(chunk) + end + end + end + end + + if a.code.to_i != 200 + puts " ⚠️ Skipping:HTTP code #{a.code} receivedfor thumbnail :/" + next + end + + # write datapackage.json + FileUtils.mkdir_p(output_dir) # Create directory (including parents) + File.write(output_file_path, JSON.pretty_generate(output_data)) + puts " ✅ HTTP 200: Wrote #{output_file_path}" + + # write janusxr.html + scene.jml + templateDir = File.dirname(__FILE__)+"/../../templates/" + FileUtils.mkdir_p(output_dir+"/.xrforge") # Create directory (including parents) + data = output_data + data['title'] = output_data['name'] + $links = [] + janusweb_src = ENV['DEV'] ? "/mnt/janusweb/janusweb.js" : "/mnt/janusweb/janusweb.min.js" + jml = ERB.new( File.read( templateDir+"JML/portalAR.erb" ) ).result(binding) + html = ERB.new( File.read( templateDir+"JML/client.html") ).result(binding) + File.write(output_dir+"/.xrforge/scene.jml", jml ) + puts " ✅ Wrote #{output_dir}/.xrforge/scene.jml" + File.write(output_dir+"/.xrforge/janusxr.html", html ) + puts " ✅ Wrote #{output_dir}/.xrforge/janusxr.html" + + rescue StandardError => e + puts " ❌ Error writing output file: #{e.message}" + end + rescue StandardError => e + # Optionally handle error quietly or output e.message + end + +end + diff --git a/manyfold/root/instances/vesta.janusxr.org/spyduck/lains-bedroom/room.yaml b/manyfold/root/instances/vesta.janusxr.org/spyduck/lains-bedroom/room.yaml new file mode 100644 index 0000000..03b8b3e --- /dev/null +++ b/manyfold/root/instances/vesta.janusxr.org/spyduck/lains-bedroom/room.yaml @@ -0,0 +1,4 @@ +url: https://vesta.janusxr.org/spyduck/lains-bedroom-serial-experiments-lain +title: "Lain's Bedroom" +description: "Recreation of the protagonist's bedroom from Serial Experiments Lain" +thumbnail: https://vesta.janusxr.org/assets/thumbs/spyduck/c59d083f4c3e9fcde3f525d28d8acd69.jpg diff --git a/manyfold/root/templates/JML/client.html b/manyfold/root/templates/JML/client.html new file mode 100644 index 0000000..f1ec38e --- /dev/null +++ b/manyfold/root/templates/JML/client.html @@ -0,0 +1,62 @@ + + + + <%=data['title']%> - JanusXR + + + + + + + + + + <%=jml%> + + + <% $links.each do |link| %> + + <% end %> + + + + + + + + diff --git a/manyfold/root/templates/JML/header.erb b/manyfold/root/templates/JML/header.erb new file mode 100644 index 0000000..a074e60 --- /dev/null +++ b/manyfold/root/templates/JML/header.erb @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/manyfold/root/templates/JML/portalAR.erb b/manyfold/root/templates/JML/portalAR.erb new file mode 100644 index 0000000..5feaa8b --- /dev/null +++ b/manyfold/root/templates/JML/portalAR.erb @@ -0,0 +1,8 @@ + + + + + + <%=data['description']%> + + diff --git a/manyfold/root/templates/JML/room.erb b/manyfold/root/templates/JML/room.erb new file mode 100644 index 0000000..9618708 --- /dev/null +++ b/manyfold/root/templates/JML/room.erb @@ -0,0 +1,10 @@ + + + <%=assets%> + + + <%=private%> <%=showavatar%> <%=startpos%>> + <%=objects%> + + + diff --git a/manyfold/root/templates/JML/room.xrforge.erb b/manyfold/root/templates/JML/room.xrforge.erb new file mode 100644 index 0000000..9618708 --- /dev/null +++ b/manyfold/root/templates/JML/room.xrforge.erb @@ -0,0 +1,10 @@ + + + <%=assets%> + + + <%=private%> <%=showavatar%> <%=startpos%>> + <%=objects%> + + + diff --git a/manyfold/root/templates/audiovisual/audiovisual.xml b/manyfold/root/templates/audiovisual/audiovisual.xml new file mode 100644 index 0000000..0f35b57 --- /dev/null +++ b/manyfold/root/templates/audiovisual/audiovisual.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + This is a template for audiovisual experiences. Use it for beautiful spatial music/video releases, by just upload/replacing an audio- or video-file. + + + +Audiovisual diff --git a/manyfold/root/templates/audiovisual/lobby.png b/manyfold/root/templates/audiovisual/lobby.png new file mode 100644 index 0000000..20c3fe7 Binary files /dev/null and b/manyfold/root/templates/audiovisual/lobby.png differ diff --git a/manyfold/root/templates/audiovisual/roundedplane.glb b/manyfold/root/templates/audiovisual/roundedplane.glb new file mode 100644 index 0000000..c9e63e3 Binary files /dev/null and b/manyfold/root/templates/audiovisual/roundedplane.glb differ diff --git a/manyfold/root/templates/audiovisual/video.mp4 b/manyfold/root/templates/audiovisual/video.mp4 new file mode 100644 index 0000000..bb69769 Binary files /dev/null and b/manyfold/root/templates/audiovisual/video.mp4 differ diff --git a/manyfold/root/templates/audiovisual/video.webm b/manyfold/root/templates/audiovisual/video.webm new file mode 100644 index 0000000..0b63817 Binary files /dev/null and b/manyfold/root/templates/audiovisual/video.webm differ diff --git a/manyfold/root/templates/thumbnailseed.jpg b/manyfold/root/templates/thumbnailseed.jpg new file mode 100644 index 0000000..c7308a6 Binary files /dev/null and b/manyfold/root/templates/thumbnailseed.jpg differ