分析 Movian 插件 somafm
native
fs.js
中对文件的操作是通过 var fs = require('native/fs');
来实现的,而 native
通过 duktape
来实现
目录
movian > ll src/ecmascript/es_*
1.9K 10 月 13 15:20 src/ecmascript/es_console.c
3.5K 10 月 13 15:20 src/ecmascript/es_crypto.c
15K 10 月 13 15:20 src/ecmascript/es_faprovider.c
8.0K 10 月 13 15:20 src/ecmascript/es_fs.c
9.7K 10 月 13 15:20 src/ecmascript/es_gumbo.c
3.3K 10 月 13 15:20 src/ecmascript/es_hook.c
4.5K 10 月 13 15:20 src/ecmascript/es_htsmsg.c
21K 10 月 13 15:20 src/ecmascript/es_io.c
3.3K 10 月 13 15:20 src/ecmascript/es_kvstore.c
3.0K 10 月 13 15:20 src/ecmascript/es_metadata.c
7.1K 10 月 13 15:20 src/ecmascript/es_misc.c
3.6K 10 月 13 15:20 src/ecmascript/es_native_obj.c
24K 10 月 13 15:20 src/ecmascript/es_prop.c
1.9K 10 月 13 15:20 src/ecmascript/es_root.c
5.4K 10 月 13 15:20 src/ecmascript/es_route.c
2.3K 10 月 13 15:20 src/ecmascript/es_scrobble.c
1.4K 10 月 13 15:20 src/ecmascript/es_searcher.c
3.9K 10 月 13 15:20 src/ecmascript/es_service.c
7.3K 10 月 13 15:20 src/ecmascript/es_sqlite.c
3.8K 10 月 13 15:20 src/ecmascript/es_stats.c
8.2K 10 月 13 15:20 src/ecmascript/es_string.c
6.2K 10 月 13 15:20 src/ecmascript/es_subtitles.c
4.9K 10 月 13 15:20 src/ecmascript/es_timer.c
24K 10 月 13 15:20 src/ecmascript/es_websocket.c
例如 es_fs.c
实现模块 native/fs
static const duk_function_list_entry fnlist_fs[] = {
{ "open", es_file_open, 3 },
{ "read", es_file_read, 5 },
{ "write", es_file_write, 5 },
{ "fsize", es_file_fsize, 1 },
{ "ftrunctae", es_file_ftruncate, 2 },
{ "rename", es_file_rename, 2 },
{ "mkdirs", es_file_mkdirs, 2 },
{ "dirname", es_file_dirname, 1 },
{ "basename", es_file_basename, 1 },
{ "copyfile", es_file_copy, 2 },
{ NULL, NULL, 0}
};
ES_MODULE("fs", fnlist_fs);
Link es_modsearch
Duktape
- Property Description
- version Duktape version number: (major * 10000) + (minor * 100) + patch.
- env Cryptic, version dependent summary of most important effective options like endianness and architecture.
- fin Set or get finalizer of an object.
- enc Encode a value (hex, base-64, JX, JC): Duktape.enc(‘hex’, ‘foo’).
- dec Decode a value (hex, base-64, JX, JC): Duktape.dec(‘base64’, ‘Zm9v’).
- info Get internal information (such as heap address and alloc size) of a value in a version specific format. The C API equivalent is duk_inspect_value().
- act Get information about call stack entry.
- gc Trigger mark-and-sweep garbage collection.
- compact Compact the memory allocated for a value (object).
- errCreate Callback to modify/replace a created error.
- errThrow Callback to modify/replace an error about to be thrown.
- Pointer Pointer constructor (function).
- Thread Thread constructor (function).
static void es_create_env(es_context_t *ec, const char *loaddir, const char *storage)
if(storage != NULL) {
duk_push_string(ctx, storage);
duk_put_prop_string(ctx, obj_idx, "storagePath");
}
duk_put_function_list(ctx, obj_idx, fnlist_core);
duk_put_prop_string(ctx, -2, "Core");
// Initialize modSearch helper
duk_get_prop_string(ctx, -1, "Duktape");
duk_push_c_function(ctx, es_modsearch, 4);
duk_put_prop_string(ctx, -2, "modSearch");
duk_pop(ctx);
duk_put_function_list(ctx, -1, es_fnlist_timer);
duk_push_object(ctx);
duk_put_function_list(ctx, -1, es_fnlist_console);
duk_put_prop_string(ctx, -2, "console");
api-v1.js
var prop = require('movian/prop');
var cryptodigest = function(algo, str) {
var crypto = require('native/crypto');
var hash = crypto.hashCreate(algo);
crypto.hashUpdate(hash, str);
var digest = crypto.hashFinalize(hash);
return Duktape.enc('hex', digest);
}
var misc = require('native/misc');
var string = require('native/string');
var popup = require('native/popup');
showtime
showtime = {
// 打印
print: print,
// JSON 解码与编码
JSONDecode: JSON.parse,
JSONEncode: JSON.stringify,
// http Get from URL
httpGet: function(url, args, headers, ctrl) {
var c = {
args: args,
headers: headers
};
for(var p in ctrl)
c[p] = ctrl[p];
return require('movian/http').request(url, c);
},
// Core object from Fun:es_create_env
currentVersionInt: Core.currentVersionInt,
currentVersionString: Core.currentVersionString,
deviceId: Core.deviceId,
httpReq: function(url, ctrl, cb) {
return require('movian/http').request(url, ctrl, cb);
},
entityDecode: string.entityDecode,
queryStringSplit: string.queryStringSplit,
pathEscape: string.pathEscape,
paramEscape: string.paramEscape,
durationToString: string.durationToString,
message: popup.message,
textDialog: popup.textDialog,
notify: popup.notify,
probe: require('native/io').probe,
print: print,
trace: console.log,
basename: require('native/fs').basename,
// sha1
sha1digest: function(str) {
return cryptodigest('sha1', str);
},
// md5
md5digest: function(str) {
return cryptodigest('md5', str);
},
RichText: function(x) {
this.str = x.toString();
},
systemIpAddress: function() {
return misc.systemIpAddress();
},
getSubtitleLanguages: require('native/subtitle').getLanguages,
xmlrpc: function() {
var a = [];
for(var i = 2; i < arguments.length; i++)
a.push(arguments[i]);
var json = JSON.stringify(a);
var x = require('native/io').xmlrpc(arguments[0], arguments[1], json);
return require('movian/xml').htsmsg(x);
},
sleep: function(x) {
return Core.sleep(x);
}
};
plugin
var plugin = {
// Fun: es_service_create
createService: function(title, url, type, enabled, icon) {
return require('movian/service').create(title, url, type, enabled, icon);
},
// Fun: es_file_mkdirs
createStore: function(name) {
return require('movian/store').create(name);
},
// page.js Route Fun: es_route_create
addURI: function(re, callback) {
var page = require('movian/page');
return new page.Route(re, callback);
},
// page.js Searcher Fun: es_hook_register
addSearcher: function(title, icon, cb) {
var page = require('movian/page');
return new page.Searcher(title, icon,cb);
},
// Plugin object from Fun: ecmascript_plugin_load
path: Plugin.path,
// parse JSON from Plugin.manifest
getDescriptor: function() {
if(this.descriptor === undefined)
this.descriptor = JSON.parse(Plugin.manifest);
return this.descriptor;
},
//popup object Fun: es_getAuthCredentials [fnlist_popup] file: es_misc.c
getAuthCredentials: popup.getAuthCredentials,
// Fun: es_http_inspector_create file: es_io.c
addHTTPAuth: require('native/io').httpInspectorCreate,
// Fun: es_file_copy file: es_fs.c
copyFile: require('native/fs').copyfile,
// Fun: es_select_view file: es_misc.c
selectView: misc.selectView,
// settings.js
createSettings: function(title, icon, description) {
var settings = require('movian/settings');
return new settings.globalSettings(Plugin.id, title, icon, description);
},
// Fun: es_cachePut
cachePut: function(stash, key, obj, maxage) {
misc.cachePut('plugin/' + Plugin.id + '/' + stash,
key, JSON.stringify(obj), maxage);
},
// Fun: es_cacheGet
cacheGet: function(stash, key) {
var v = misc.cacheGet('plugin/' + Plugin.id + '/' + stash, key);
return v ? JSON.parse(v) : null;
},
config: {},
properties: prop.global.plugin[Plugin.id],
// itemhook.js
addItemHook: function(conf) {
require('movian/itemhook').create(conf);
},
// Fun: es_subtitleprovideradd
addSubtitleProvider: function(fn) {
require('native/subtitle').addProvider(function(root, query, basescore, autosel) {
var req = Object.create(query);
req.addSubtitle = function(url, title, language, format,
source, score) {
require('native/subtitle').addItem(root, url, title, language, format, source,
basescore + score, autosel);
}
fn(req);
}, Plugin.id, Plugin.id);
}
};
// This is the return value
plugin;
somafm.js
可以使用 showtime.print(Plugin.manifest);
在 shell
输出信息
(function(plugin) {
var BASE_URL = "http://www.somafm.com";
var logo = plugin.path + "somafm.png";
// Create Service for Plugin somafm
plugin.createService(plugin.getDescriptor().title, plugin.getDescriptor().id + ":start", "music", true, logo);
function descr(s) {
var tmp = s.match(/<p class="descr">([\S\s]*?)<dl>/);
if (tmp) return tmp[1].replace("<!--","").replace("-->","").replace("</p>","").replace(/^\s+|\s+$/g, '');
tmp = s.match(/<h1>([\S\s]*?)<\/h1>/);
if (tmp) return tmp[1];
return null;
}
// Start page
// URI: [soamfm:start]
plugin.addURI(plugin.getDescriptor().id + ":start", function(page) {
page.type = "directory";
page.metadata.title = plugin.getDescriptor().title;
page.metadata.logo = logo;
page.loading = true;
if (showtime.currentVersionInt < 49900000) {
page.metadata.glwview = plugin.path + 'views/array.view';
page.contents = 'items';
page.options.createInt('childTilesX', 'Tiles by X', 6, 1, 10, 1, '', function(v) {
page.metadata.childTilesX = v;
}, true);
page.options.createInt('childTilesY', 'Tiles by Y', 2, 1, 4, 1, '', function(v) {
page.metadata.childTilesY = v;
}, true);
page.options.createBool('informationBar', 'Information Bar', 1, function(v) {
page.metadata.informationBar = v;
}, true);
} else
page.model.contents = 'grid';
// 请求网页数据 http://somafm.com/listen/
var doc = showtime.httpReq(BASE_URL + "/listen").toString();
// re 筛选数据
// 1-id, 2-listeners, 3-icon, 4-title, 5-(description/now playing)
var re = /<!-- Channel: (.*) Listeners: (.*) -->[\S\s]*?<img src="([\S\s]*?)"[\S\s]*?<h3>([\S\s]*?)<\/h3>([\S\s]*?)<\/li>/g;
var match = re.exec(doc);
while (match) {
page.appendItem("icecast:" + BASE_URL + "/startstream=" + match[1] + ".pls", "station", {
station: match[4],
title: match[4],
description: descr(match[5]),
icon: BASE_URL + match[3],
album_art: BASE_URL + match[3],
nowplaying: (match[5].match(/<span class="playing"><a href="[\S\s]*?">([\S\s]*?)<\/a>/) ? match[5].match(/<span class="playing"><a href="[\S\s]*?">([\S\s]*?)<\/a>/)[1] : null),
listeners: match[2]
});
match = re.exec(doc);
};
page.loading = false;
});
})(this);