Skip to content

API v1 to API v2 Migration Guide

Overview

This guide helps you migrate existing Movian plugins from the legacy API v1 to the modern API v2. The migration typically takes 1-4 hours for a standard plugin and provides significant benefits in performance, maintainability, and access to modern features.

Why Migrate?

Benefits of API v2

  • Better Performance: No emulation layer overhead
  • Modern ECMAScript: Full ES5 support + extensions
  • Module System: Clean, organized code with require()
  • Better Debugging: Clearer stack traces and error messages
  • New Features: Access to APIs not available in v1
  • Future-Proof: Active development and support
  • Best Practices: Follows modern JavaScript patterns

API v1 Limitations

  • ⚠️ Deprecated: No new features added
  • ⚠️ Performance: Emulation layer adds overhead
  • ⚠️ Global Pollution: All APIs in global scope
  • ⚠️ Limited Features: Cannot access new API v2 capabilities

Quick Start

Minimum Changes Required

For a basic plugin, you need to:

  1. Update plugin.json apiversion field
  2. Convert require() statements to use module paths
  3. Replace showtime.* calls with module methods
  4. Replace plugin.* calls with module methods

Example: Minimal Migration

Before (API v1):

// plugin.json
{
  "apiversion": 1
}

// main.js
var response = showtime.httpGet(url);
plugin.createService("My Service", "myservice:", "video", true);

After (API v2):

// plugin.json
{
  "apiversion": 2
}

// main.js
var http = require('movian/http');
var service = require('movian/service');

var response = http.request(url);
service.create("My Service", "myservice:", "video", true);

Step-by-Step Migration

Step 1: Update plugin.json

Change the apiversion field from 1 to 2:

{
  "id": "my-plugin",
  "version": "1.0",
  "type": "ecmascript",
  "apiversion": 2,
  "title": "My Plugin"
}

Step 2: Add Module Imports

At the top of your main.js, add require() statements for modules you'll use:

// Common modules
var http = require('movian/http');
var service = require('movian/service');
var page = require('movian/page');
var store = require('movian/store');
var settings = require('movian/settings');

// Native modules (as needed)
var popup = require('native/popup');
var string = require('native/string');
var fs = require('native/fs');

Step 3: Replace Global Calls

Replace all showtime.* and plugin.* calls with module equivalents.

See Breaking Changes section for complete mapping.

Step 4: Update Plugin Metadata Access

API v1:

var descriptor = plugin.getDescriptor();
var pluginPath = plugin.path;

API v2:

var descriptor = JSON.parse(Plugin.manifest);
var pluginPath = Plugin.path;
var pluginId = Plugin.id;

Step 5: Test Thoroughly

  • Test all plugin functionality
  • Check error handling
  • Verify HTTP requests work
  • Test settings (if applicable)
  • Verify page navigation
  • Check service registration

Breaking Changes

1. Module System

The biggest change is the introduction of CommonJS modules.

API v1 (Global objects):

// Everything available globally
var response = showtime.httpGet(url);
var service = plugin.createService(...);

API v2 (Module system):

// Import modules explicitly
var http = require('movian/http');
var service = require('movian/service');

var response = http.request(url);
service.create(...);

2. HTTP Requests

API v1:

var response = showtime.httpGet(url);
var response = showtime.httpGet(url, args, headers, ctrl);

API v2:

var http = require('movian/http');

// Simple GET
var response = http.request(url);

// With options
var response = http.request(url, {
  args: args,
  headers: headers,
  method: 'GET'
});

Advanced HTTP:

API v1:

showtime.httpReq(url, {
  method: 'POST',
  postdata: data,
  headers: {'Content-Type': 'application/json'}
}, function(err, response) {
  if (err) {
    showtime.print("Error: " + err);
  } else {
    showtime.print("Response: " + response);
  }
});

API v2:

var http = require('movian/http');

http.request(url, {
  method: 'POST',
  postdata: data,
  headers: {'Content-Type': 'application/json'}
}, function(err, response) {
  if (err) {
    console.log("Error:", err);
  } else {
    console.log("Response:", response);
  }
});

3. Service Registration

API v1:

var service = plugin.createService(
  "My Service",
  "myservice:start",
  "video",
  true,
  plugin.path + "icon.png"
);

API v2:

var service = require('movian/service');

service.create(
  "My Service",
  "myservice:start",
  "video",
  true,
  Plugin.path + "icon.png"
);

4. Page Routes

API v1:

plugin.addURI("myservice:start", function(page) {
  page.type = "directory";
  page.contents = "items";
  page.metadata.title = "My Service";
});

API v2:

var page = require('movian/page');

new page.Route("myservice:start", function(page) {
  page.type = "directory";
  page.contents = "items";
  page.metadata.title = "My Service";
});

5. Search Providers

API v1:

plugin.addSearcher("My Search", plugin.path + "icon.png", function(page, query) {
  // Search implementation
});

API v2:

var page = require('movian/page');

new page.Searcher("My Search", Plugin.path + "icon.png", function(page, query) {
  // Search implementation
});

6. Settings

API v1:

var settings = plugin.createSettings(
  "My Plugin Settings",
  plugin.path + "icon.png",
  "Configure my plugin"
);

settings.createString("username", "Username", "", function(v) {
  // Handle change
});

API v2:

var settings = require('movian/settings');

var s = new settings.globalSettings(
  Plugin.id,
  "My Plugin Settings",
  Plugin.path + "icon.png",
  "Configure my plugin"
);

s.createString("username", "Username", "", function(v) {
  // Handle change
});

7. Storage/Store

API v1:

var store = plugin.createStore("mydata");
store.mykey = "myvalue";
var value = store.mykey;

API v2:

var store = require('movian/store');

var s = store.create("mydata");
s.mykey = "myvalue";
var value = s.mykey;

8. Cache

API v1:

// Store in cache
plugin.cachePut("stash", "key", {data: "value"}, 3600);

// Retrieve from cache
var data = plugin.cacheGet("stash", "key");

API v2:

var misc = require('native/misc');

// Store in cache (manual JSON serialization)
misc.cachePut(
  'plugin/' + Plugin.id + '/stash',
  'key',
  JSON.stringify({data: "value"}),
  3600
);

// Retrieve from cache
var json = misc.cacheGet('plugin/' + Plugin.id + '/stash', 'key');
var data = json ? JSON.parse(json) : null;

9. String Utilities

API v1:

var decoded = showtime.entityDecode(html);
var params = showtime.queryStringSplit(qs);
var escaped = showtime.pathEscape(path);
var duration = showtime.durationToString(seconds);

API v2:

var string = require('native/string');

var decoded = string.entityDecode(html);
var params = string.queryStringSplit(qs);
var escaped = string.pathEscape(path);
var duration = string.durationToString(seconds);

10. Popup Dialogs

API v1:

showtime.message("Hello World", true, false);
showtime.notify("Notification", 2);

var text = showtime.textDialog("Enter text:", true);

var creds = plugin.getAuthCredentials("Login Required", "Enter credentials", false);

API v2:

var popup = require('native/popup');

popup.message("Hello World", true, false);
popup.notify("Notification", 2);

var text = popup.textDialog("Enter text:", true);

var creds = popup.getAuthCredentials("Login Required", "Enter credentials", false);

11. Cryptography

API v1:

var hash = showtime.md5digest(str);
var hash = showtime.sha1digest(str);

API v2:

var crypto = require('native/crypto');

// MD5
var md5hash = crypto.hashCreate('md5');
crypto.hashUpdate(md5hash, str);
var md5digest = crypto.hashFinalize(md5hash);
var md5hex = Duktape.enc('hex', md5digest);

// SHA1
var sha1hash = crypto.hashCreate('sha1');
crypto.hashUpdate(sha1hash, str);
var sha1digest = crypto.hashFinalize(sha1hash);
var sha1hex = Duktape.enc('hex', sha1digest);

12. File System

API v1:

var basename = showtime.basename(path);
plugin.copyFile(src, dst);

API v2:

var fs = require('native/fs');

var basename = fs.basename(path);
fs.copyfile(src, dst);

13. Logging

API v1:

showtime.print("Message");
showtime.trace("Debug message");

API v2:

console.log("Message");
console.log("Debug message");

// Also available:
console.error("Error message");
console.warn("Warning message");

14. JSON Operations

API v1:

var obj = showtime.JSONDecode(jsonString);
var json = showtime.JSONEncode(obj);

API v2:

// Native JavaScript
var obj = JSON.parse(jsonString);
var json = JSON.stringify(obj);

15. System Information

API v1:

var version = showtime.currentVersionString;
var versionInt = showtime.currentVersionInt;
var deviceId = showtime.deviceId;

API v2:

// Global Core object
var version = Core.currentVersionString;
var versionInt = Core.currentVersionInt;
var deviceId = Core.deviceId;

16. Rich Text

API v1:

var rt = new showtime.RichText("Hello <b>World</b>");
page.metadata.title = rt;

API v2:

var prop = require('movian/prop');

var rt = new prop.RichText("Hello <b>World</b>");
page.metadata.title = rt;

17. Item Hooks

API v1:

plugin.addItemHook({
  title: "My Action",
  handler: function(item) {
    // Handle action
  }
});

API v2:

var itemhook = require('movian/itemhook');

itemhook.create({
  title: "My Action",
  handler: function(item) {
    // Handle action
  }
});

18. Subtitle Providers

API v1:

plugin.addSubtitleProvider(function(req) {
  req.addSubtitle(url, title, language, format, source, score);
});

API v2:

var subtitle = require('native/subtitle');

subtitle.addProvider(function(root, query, basescore, autosel) {
  subtitle.addItem(root, url, title, language, format, source,
                   basescore + score, autosel);
}, Plugin.id, Plugin.id);

Complete API Mapping Reference

See API Differences for a complete table of all API v1 methods and their API v2 equivalents.

Common Migration Patterns

Pattern 1: Simple HTTP GET

API v1:

(function(plugin) {
  var response = showtime.httpGet("https://api.example.com/data");
  var data = showtime.JSONDecode(response);

  plugin.addURI("myplugin:start", function(page) {
    page.type = "directory";
    page.metadata.title = data.title;
  });
})(this);

API v2:

var http = require('movian/http');
var page = require('movian/page');

var response = http.request("https://api.example.com/data");
var data = JSON.parse(response);

new page.Route("myplugin:start", function(page) {
  page.type = "directory";
  page.metadata.title = data.title;
});

Pattern 2: Service with Settings

API v1:

(function(plugin) {
  var settings = plugin.createSettings("My Plugin", plugin.path + "icon.png");
  var username = settings.createString("username", "Username", "");

  var service = plugin.createService("My Service", "myservice:", "video", true);

  plugin.addURI("myservice:start", function(page) {
    showtime.print("Username: " + username.value);
  });
})(this);

API v2:

var settings = require('movian/settings');
var service = require('movian/service');
var page = require('movian/page');

var s = new settings.globalSettings(Plugin.id, "My Plugin", Plugin.path + "icon.png");
var username = s.createString("username", "Username", "");

service.create("My Service", "myservice:", "video", true);

new page.Route("myservice:start", function(page) {
  console.log("Username:", username.value);
});

Pattern 3: Search with HTTP

API v1:

(function(plugin) {
  plugin.addSearcher("My Search", plugin.path + "icon.png", function(page, query) {
    var url = "https://api.example.com/search?q=" + showtime.paramEscape(query);
    var response = showtime.httpGet(url);
    var data = showtime.JSONDecode(response);

    data.results.forEach(function(item) {
      page.appendItem(item.url, "video", {
        title: item.title,
        description: item.description
      });
    });
  });
})(this);

API v2:

var page = require('movian/page');
var http = require('movian/http');
var string = require('native/string');

new page.Searcher("My Search", Plugin.path + "icon.png", function(page, query) {
  var url = "https://api.example.com/search?q=" + string.paramEscape(query);
  var response = http.request(url);
  var data = JSON.parse(response);

  data.results.forEach(function(item) {
    page.appendItem(item.url, "video", {
      title: item.title,
      description: item.description
    });
  });
});

Pattern 4: Persistent Storage

API v1:

(function(plugin) {
  var store = plugin.createStore("favorites");

  plugin.addURI("myplugin:add", function(page) {
    store.favorites = store.favorites || [];
    store.favorites.push({id: 123, title: "Item"});
  });

  plugin.addURI("myplugin:list", function(page) {
    var favorites = store.favorites || [];
    favorites.forEach(function(item) {
      page.appendItem("", "video", {title: item.title});
    });
  });
})(this);

API v2:

var store = require('movian/store');
var page = require('movian/page');

var s = store.create("favorites");

new page.Route("myplugin:add", function(page) {
  s.favorites = s.favorites || [];
  s.favorites.push({id: 123, title: "Item"});
});

new page.Route("myplugin:list", function(page) {
  var favorites = s.favorites || [];
  favorites.forEach(function(item) {
    page.appendItem("", "video", {title: item.title});
  });
});

Migration Checklist

Use this checklist to ensure complete migration:

plugin.json

  • [ ] Update "apiversion": 1 to "apiversion": 2
  • [ ] Verify all other fields are correct

Module Imports

  • [ ] Add require() statements for all needed modules
  • [ ] Remove any old var plugin = this; patterns
  • [ ] Organize imports at top of file

Global Object Replacements

  • [ ] Replace all showtime.httpGet() with http.request()
  • [ ] Replace all showtime.httpReq() with http.request()
  • [ ] Replace all showtime.print() with console.log()
  • [ ] Replace all showtime.trace() with console.log()
  • [ ] Replace all showtime.JSONDecode() with JSON.parse()
  • [ ] Replace all showtime.JSONEncode() with JSON.stringify()
  • [ ] Replace all showtime.entityDecode() with string.entityDecode()
  • [ ] Replace all showtime.queryStringSplit() with string.queryStringSplit()
  • [ ] Replace all showtime.pathEscape() with string.pathEscape()
  • [ ] Replace all showtime.paramEscape() with string.paramEscape()
  • [ ] Replace all showtime.message() with popup.message()
  • [ ] Replace all showtime.notify() with popup.notify()
  • [ ] Replace all showtime.textDialog() with popup.textDialog()
  • [ ] Replace all showtime.md5digest() with crypto module
  • [ ] Replace all showtime.sha1digest() with crypto module
  • [ ] Replace all showtime.basename() with fs.basename()
  • [ ] Replace showtime.currentVersionString with Core.currentVersionString
  • [ ] Replace showtime.deviceId with Core.deviceId

Plugin Object Replacements

  • [ ] Replace plugin.createService() with service.create()
  • [ ] Replace plugin.createStore() with store.create()
  • [ ] Replace plugin.addURI() with new page.Route()
  • [ ] Replace plugin.addSearcher() with new page.Searcher()
  • [ ] Replace plugin.createSettings() with new settings.globalSettings()
  • [ ] Replace plugin.cachePut() with misc.cachePut() + JSON
  • [ ] Replace plugin.cacheGet() with misc.cacheGet() + JSON
  • [ ] Replace plugin.path with Plugin.path
  • [ ] Replace plugin.getDescriptor() with JSON.parse(Plugin.manifest)
  • [ ] Replace plugin.getAuthCredentials() with popup.getAuthCredentials()
  • [ ] Replace plugin.copyFile() with fs.copyfile()
  • [ ] Replace plugin.addItemHook() with itemhook.create()
  • [ ] Replace plugin.addSubtitleProvider() with subtitle.addProvider()

Testing

  • [ ] Test service registration
  • [ ] Test all page routes
  • [ ] Test HTTP requests
  • [ ] Test settings (if applicable)
  • [ ] Test storage/store (if applicable)
  • [ ] Test search (if applicable)
  • [ ] Test error handling
  • [ ] Test with different Movian versions (if possible)

Code Quality

  • [ ] Remove any unused require() statements
  • [ ] Add error handling for HTTP requests
  • [ ] Use console.log() for debugging
  • [ ] Follow modern JavaScript patterns
  • [ ] Add comments for complex logic

Troubleshooting

Common Issues

Issue: "require is not defined"

Cause: Forgot to update apiversion in plugin.json

Solution:

{
  "apiversion": 2
}

Issue: "showtime is not defined"

Cause: Using API v1 syntax with API v2

Solution: Replace with module imports:

var http = require('movian/http');
var popup = require('native/popup');
// etc.

Issue: "plugin is not defined"

Cause: Using API v1 plugin object with API v2

Solution: Use Plugin (capital P) or module methods:

// Instead of plugin.path
var path = Plugin.path;

// Instead of plugin.createService()
var service = require('movian/service');
service.create(...);

Issue: Cache not working

Cause: API v2 requires manual JSON serialization

Solution:

var misc = require('native/misc');

// Store
misc.cachePut('plugin/' + Plugin.id + '/stash', 'key',
              JSON.stringify(data), maxage);

// Retrieve
var json = misc.cacheGet('plugin/' + Plugin.id + '/stash', 'key');
var data = json ? JSON.parse(json) : null;

Issue: HTTP requests failing

Cause: Different parameter format in API v2

Solution:

var http = require('movian/http');

// API v2 uses options object
var response = http.request(url, {
  method: 'GET',
  headers: {'User-Agent': 'MyPlugin'},
  args: {param: 'value'}
});

Debugging Tips

  1. Use console.log() extensively during migration
  2. Test incrementally - migrate one function at a time
  3. Check Movian logs for error messages
  4. Verify module paths - use movian/* or native/*
  5. Test with simple cases first before complex logic

Testing Your Migration

Basic Functionality Test

var http = require('movian/http');
var service = require('movian/service');
var page = require('movian/page');

console.log("Plugin loaded:", Plugin.id);
console.log("Movian version:", Core.currentVersionString);

service.create("Test Service", "test:start", "video", true);

new page.Route("test:start", function(page) {
  console.log("Page opened");
  page.type = "directory";
  page.metadata.title = "Test Page";

  try {
    var response = http.request("https://httpbin.org/get");
    console.log("HTTP test passed");
  } catch(e) {
    console.error("HTTP test failed:", e);
  }
});

Validation Script

Create a test checklist:

var tests = {
  "Module loading": function() {
    var http = require('movian/http');
    return http !== undefined;
  },
  "Plugin metadata": function() {
    return Plugin.id !== undefined && Plugin.path !== undefined;
  },
  "Core object": function() {
    return Core.currentVersionString !== undefined;
  },
  "Console logging": function() {
    console.log("Test");
    return true;
  },
  "JSON operations": function() {
    var obj = JSON.parse('{"test": true}');
    return obj.test === true;
  }
};

for (var name in tests) {
  try {
    if (tests[name]()) {
      console.log("✅", name);
    } else {
      console.log("❌", name);
    }
  } catch(e) {
    console.log("❌", name, "-", e);
  }
}

Performance Considerations

API v2 Performance Benefits

  • No emulation overhead: Direct module calls
  • Better memory usage: Modules loaded on demand
  • Faster startup: No global object construction
  • Optimized runtime: Native Duktape performance

Benchmarking

API v1 (with emulation):

var start = Date.now();
for (var i = 0; i < 1000; i++) {
  showtime.httpGet(url);  // Goes through emulation layer
}
var time = Date.now() - start;

API v2 (native):

var http = require('movian/http');
var start = Date.now();
for (var i = 0; i < 1000; i++) {
  http.request(url);  // Direct call
}
var time = Date.now() - start;

Typical improvement: 10-30% faster for API-heavy operations.

Best Practices for API v2

1. Module Organization

// Group related imports
var http = require('movian/http');
var service = require('movian/service');
var page = require('movian/page');

// Native modules separate
var popup = require('native/popup');
var string = require('native/string');

// Constants
var BASE_URL = "https://api.example.com";
var CACHE_TIME = 3600;

// Helper functions
function fetchData(endpoint) {
  return http.request(BASE_URL + endpoint);
}

// Main logic
service.create("My Service", "myservice:", "video", true);

2. Error Handling

var http = require('movian/http');
var popup = require('native/popup');

new page.Route("myservice:start", function(page) {
  try {
    var response = http.request(url);
    var data = JSON.parse(response);
    // Process data...
  } catch(e) {
    console.error("Error:", e);
    popup.notify("Failed to load data: " + e, 2, undefined);
    page.error("Failed to load content");
  }
});

3. Async Patterns

var http = require('movian/http');

// Callback pattern
http.request(url, {}, function(err, response) {
  if (err) {
    console.error("Error:", err);
    return;
  }
  var data = JSON.parse(response);
  // Process data...
});

4. Configuration

var settings = require('movian/settings');

var config = new settings.globalSettings(
  Plugin.id,
  "My Plugin",
  Plugin.path + "icon.png"
);

var apiKey = config.createString("apikey", "API Key", "", function(v) {
  console.log("API Key updated:", v);
});

// Use in code
function makeRequest() {
  if (!apiKey.value) {
    popup.notify("Please configure API key in settings", 2);
    return;
  }
  // Make request with apiKey.value...
}

See Also

Source References

  • API v1 Emulation: movian/res/ecmascript/legacy/api-v1.js
  • API Version Constants: movian/src/ecmascript/ecmascript.h:227
  • Runtime Implementation: movian/src/ecmascript/ecmascript.c

Status: ✅ Ready for Use
Last Updated: 2025-11-08
Movian Version: 4.8+
Accuracy: 🟢 Verified from source code and testing

Need Help?

If you encounter issues during migration:

  1. Check this guide's Troubleshooting section
  2. Review the API Differences reference
  3. Examine working plugin examples
  4. Check Movian logs for error messages
  5. Test with minimal code first, then add complexity

Migration typically takes 1-4 hours and provides significant long-term benefits!