Task 677: .SPIN File Format

Task 677: .SPIN File Format

1. List of all properties of the .SPIN file format intrinsic to its file system

The .SPIN file format is a JSON-based structure used for defining 360-degree product spins in the Sirv platform. It consists of a single top-level JSON object with the following properties and sub-properties (including types and descriptions based on the format specification):

  • pattern (string): A regular expression pattern used to match image filenames in the spin set. It identifies sequential numerals in filenames for automatic image detection.
  • layers (object): An object that maps layers (rows for multi-dimensional spins) to image references. Keys are string identifiers for layers (e.g., "1", "2"). Each layer value is an object where:
  • Keys are string column indices (e.g., "1", "2", ..., representing frame positions).
  • Values are string filenames (relative paths to images).
  • settings (object): An object containing configuration options for spin behavior. Possible sub-properties include:
  • autospin (string): Controls auto-rotation behavior (e.g., "once", "twice", "infinite", "false").
  • autospinSpeed (number): Speed of auto-rotation in milliseconds for one full cycle.
  • autospinDirection (string): Direction of auto-spin (e.g., "clockwise", "anticlockwise", "alternate-clockwise", "alternate-anticlockwise").
  • autospinStop (string): Condition to stop infinite auto-spin (e.g., "interaction", "never").
  • autospinStart (string): Trigger for starting auto-spin (e.g., "load", "click", "hover").
  • fullscreen (boolean): Enables or disables the fullscreen button.
  • hint (boolean): Shows or hides interaction hints.
  • loopColumn (boolean): Enables or disables looping on columns.
  • loopRow (boolean): Enables or disables looping on rows (for multi-row spins).
  • mousewheelStep (number): Number of frames to advance per mouse wheel tick (0 to disable).
  • retina (boolean): Enables or disables retina display optimization.
  • reverseRotation (string): Reverses rotation direction (e.g., "column", "row", "both", false).
  • rightClick (boolean): Enables or disables right-click context menu.
  • speed (number): Drag sensitivity (0-100; lower values mean faster response).
  • spin (string): Interaction mode (e.g., "drag", "hover").
  • spinOnAnyDrag (boolean): Allows spinning on drags in any direction.
  • startColumn (number): Initial starting column frame.
  • startRow (number): Initial starting row frame (for multi-row spins).
  • swapRowsColumns (boolean): Swaps interpretation of rows and columns.
  • zoom (number): Default zoom magnification level (e.g., 2.5; 1 for pixel scaling).
  • initializeOn (string): When to load images (e.g., "load", "click", "hover").
  • hintText (string): Custom text for interaction hints.
  • mobileHintText (string): Custom hint text for mobile devices.
  • loadingText (string): Text displayed during loading.
  • fullscreenLoadingText (string): Loading text in fullscreen mode.
  • Additional sub-properties may include text overlays or other custom options under nested objects like "image.text" (for profiles, but adaptable), with fields like position, color, opacity, etc.

These properties define the structure, image mappings, and behavioral settings of the spin. The file is typically small (1-2 KB) and must be valid JSON.

These links point to .SPIN files hosted on Sirv. Accessing them may render a viewer in browsers, but they can be downloaded directly via tools like wget/curl or by saving the source (as they serve the JSON content).

3. Ghost blog embedded HTML JavaScript for drag-and-drop .SPIN file dump

Here's a self-contained HTML snippet with embedded JavaScript that can be embedded in a Ghost blog post (or any HTML-supporting blog). It creates a drop zone where users can drag and drop a .SPIN file. The script reads the file, parses the JSON, and dumps all properties (recursively traversing objects) to the screen in a readable format.

Drag and drop a .SPIN file here

4. Python class for .SPIN file handling

import json
import os

class SpinFileHandler:
    def __init__(self, filepath):
        self.filepath = filepath
        self.data = None

    def read_and_decode(self):
        """Read and decode (parse) the .SPIN file."""
        if not os.path.exists(self.filepath):
            raise FileNotFoundError(f"File {self.filepath} not found.")
        with open(self.filepath, 'r') as f:
            self.data = json.load(f)

    def print_properties(self):
        """Print all properties recursively."""
        if self.data is None:
            print("No data loaded. Call read_and_decode() first.")
            return
        self._print_dict(self.data)

    def _print_dict(self, d, prefix=''):
        for key, value in d.items():
            if isinstance(value, dict):
                print(f"{prefix}{key}:")
                self._print_dict(value, prefix + '  ')
            else:
                print(f"{prefix}{key}: {value}")

    def write(self, new_filepath=None):
        """Write the current data back to a .SPIN file."""
        if self.data is None:
            print("No data to write.")
            return
        filepath = new_filepath or self.filepath
        with open(filepath, 'w') as f:
            json.dump(self.data, f, indent=2)
        print(f"Written to {filepath}")

# Example usage:
# handler = SpinFileHandler('example.spin')
# handler.read_and_decode()
# handler.print_properties()
# handler.data['settings']['autospin'] = 'infinite'  # Modify if needed
# handler.write('modified.spin')

5. Java class for .SPIN file handling

import org.json.JSONObject;
import org.json.JSONException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;

public class SpinFileHandler {
    private String filepath;
    private JSONObject data;

    public SpinFileHandler(String filepath) {
        this.filepath = filepath;
        this.data = null;
    }

    public void readAndDecode() throws IOException, JSONException {
        String content = new String(Files.readAllBytes(Paths.get(filepath)));
        this.data = new JSONObject(content);
    }

    public void printProperties() {
        if (data == null) {
            System.out.println("No data loaded. Call readAndDecode() first.");
            return;
        }
        printDict(data, "");
    }

    private void printDict(JSONObject obj, String prefix) {
        for (String key : obj.keySet()) {
            Object value = obj.get(key);
            if (value instanceof JSONObject) {
                System.out.println(prefix + key + ":");
                printDict((JSONObject) value, prefix + "  ");
            } else {
                System.out.println(prefix + key + ": " + value);
            }
        }
    }

    public void write(String newFilepath) throws IOException {
        if (data == null) {
            System.out.println("No data to write.");
            return;
        }
        String path = (newFilepath != null) ? newFilepath : filepath;
        try (FileWriter writer = new FileWriter(path)) {
            writer.write(data.toString(2));
        }
        System.out.println("Written to " + path);
    }

    // Example usage:
    // public static void main(String[] args) throws Exception {
    //     SpinFileHandler handler = new SpinFileHandler("example.spin");
    //     handler.readAndDecode();
    //     handler.printProperties();
    //     handler.data.put("settings", new JSONObject().put("autospin", "infinite"));  // Modify if needed
    //     handler.write("modified.spin");
    // }
}

(Note: This uses the org.json library for JSON handling. Include it in your classpath.)

6. JavaScript class for .SPIN file handling

const fs = require('fs');  // For Node.js environment

class SpinFileHandler {
  constructor(filepath) {
    this.filepath = filepath;
    this.data = null;
  }

  readAndDecode() {
    try {
      const content = fs.readFileSync(this.filepath, 'utf8');
      this.data = JSON.parse(content);
    } catch (err) {
      console.error(`Error reading/decoding file: ${err.message}`);
    }
  }

  printProperties() {
    if (!this.data) {
      console.log('No data loaded. Call readAndDecode() first.');
      return;
    }
    this._printDict(this.data);
  }

  _printDict(obj, prefix = '') {
    for (const key in obj) {
      if (typeof obj[key] === 'object' && obj[key] !== null) {
        console.log(`${prefix}${key}:`);
        this._printDict(obj[key], prefix + '  ');
      } else {
        console.log(`${prefix}${key}: ${obj[key]}`);
      }
    }
  }

  write(newFilepath = null) {
    if (!this.data) {
      console.log('No data to write.');
      return;
    }
    const path = newFilepath || this.filepath;
    fs.writeFileSync(path, JSON.stringify(this.data, null, 2));
    console.log(`Written to ${path}`);
  }
}

// Example usage:
// const handler = new SpinFileHandler('example.spin');
// handler.readAndDecode();
// handler.printProperties();
// handler.data.settings.autospin = 'infinite';  // Modify if needed
// handler.write('modified.spin');

(Note: This is for Node.js; for browser, use FileReader instead of fs.)

7. C class for .SPIN file handling

Since C does not have native classes, this is implemented as a C++ class using the nlohmann/json library for JSON parsing (include <json.hpp> and link the library). If pure C is required, a custom JSON parser would be needed, but C++ is assumed for "class" support.

#include <iostream>
#include <fstream>
#include <string>
#include <nlohmann/json.hpp>

using json = nlohmann::json;

class SpinFileHandler {
private:
    std::string filepath;
    json data;

public:
    SpinFileHandler(const std::string& fp) : filepath(fp) {}

    void readAndDecode() {
        std::ifstream file(filepath);
        if (!file.is_open()) {
            std::cerr << "File " << filepath << " not found." << std::endl;
            return;
        }
        file >> data;
        file.close();
    }

    void printProperties() {
        if (data.is_null()) {
            std::cout << "No data loaded. Call readAndDecode() first." << std::endl;
            return;
        }
        printDict(data, "");
    }

private:
    void printDict(const json& obj, const std::string& prefix) {
        for (auto it = obj.begin(); it != obj.end(); ++it) {
            if (it.value().is_object()) {
                std::cout << prefix << it.key() << ":" << std::endl;
                printDict(it.value(), prefix + "  ");
            } else {
                std::cout << prefix << it.key() << ": " << it.value() << std::endl;
            }
        }
    }

public:
    void write(const std::string& newFilepath = "") {
        if (data.is_null()) {
            std::cout << "No data to write." << std::endl;
            return;
        }
        std::string path = newFilepath.empty() ? filepath : newFilepath;
        std::ofstream outFile(path);
        outFile << data.dump(2);
        outFile.close();
        std::cout << "Written to " << path << std::endl;
    }
};

// Example usage:
// int main() {
//     SpinFileHandler handler("example.spin");
//     handler.readAndDecode();
//     handler.printProperties();
//     handler.data["settings"]["autospin"] = "infinite";  // Modify if needed
//     handler.write("modified.spin");
//     return 0;
// }