mirror of
https://kevinblog.sytes.net/Code/Jibo-Revival-Group/JiboOs.git
synced 2026-06-13 09:16:26 +00:00
plex app (forgor the rederer , comming in a bit :) )
This commit is contained in:
244
V3.1/build/opt/jibo/Jibo/Skills/@be/be/node_modules/@be/plex-music/assets/player/plexView.json
generated
vendored
Normal file
244
V3.1/build/opt/jibo/Jibo/Skills/@be/be/node_modules/@be/plex-music/assets/player/plexView.json
generated
vendored
Normal file
@@ -0,0 +1,244 @@
|
||||
{
|
||||
"viewConfig": {
|
||||
"type": "View",
|
||||
"id": "plexMusicView",
|
||||
"category": "display",
|
||||
"transitionStageOnly": true
|
||||
},
|
||||
"componentConfigs": [
|
||||
{
|
||||
"id": "debugIcon",
|
||||
"type": "Clip",
|
||||
"assets": [
|
||||
{
|
||||
"id": "debugOk",
|
||||
"src": "jibo://resources/actionIcons/ok.png",
|
||||
"type": "texture"
|
||||
}
|
||||
],
|
||||
"position": {
|
||||
"x": 30,
|
||||
"y": 30
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "debugLabel",
|
||||
"type": "Label",
|
||||
"text": "plex view",
|
||||
"style": {
|
||||
"fontSize": 28,
|
||||
"fontFamily": "Proxima Nova Soft",
|
||||
"fill": "#FFFFFF"
|
||||
},
|
||||
"position": {
|
||||
"x": 90,
|
||||
"y": 42
|
||||
},
|
||||
"targetAnchor": {
|
||||
"x": "0",
|
||||
"y": "0.5"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "bg",
|
||||
"type": "Clip",
|
||||
"assets": [
|
||||
{
|
||||
"id": "bgTexture",
|
||||
"src": "assets/player/textures/MediaServerMenu.png",
|
||||
"type": "texture"
|
||||
}
|
||||
],
|
||||
"position": {
|
||||
"x": "LEFT",
|
||||
"y": "TOP"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "title",
|
||||
"type": "Label",
|
||||
"text": "Plex Music",
|
||||
"style": {
|
||||
"fontSize": 48,
|
||||
"fontFamily": "Proxima Nova Soft",
|
||||
"fontStyle": "normal",
|
||||
"fill": "#FFFFFF"
|
||||
},
|
||||
"position": {
|
||||
"x": "CENTER",
|
||||
"y": 90
|
||||
},
|
||||
"targetAnchor": {
|
||||
"x": "0.5",
|
||||
"y": "0.5"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "status",
|
||||
"type": "Label",
|
||||
"text": "Status: not connected",
|
||||
"style": {
|
||||
"fontSize": 36,
|
||||
"fontFamily": "Proxima Nova Light",
|
||||
"fill": "#FFFFFF"
|
||||
},
|
||||
"position": {
|
||||
"x": "CENTER",
|
||||
"y": 160
|
||||
},
|
||||
"bounds": {
|
||||
"width": 1120
|
||||
},
|
||||
"targetAnchor": {
|
||||
"x": "0.5",
|
||||
"y": "0.5"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "backButton",
|
||||
"type": "ActionButton",
|
||||
"label": "Back",
|
||||
"colors": ["0x545469", "0x2A2938"],
|
||||
"iconSrc": "jibo://resources/actionIcons/cancel.png",
|
||||
"position": {
|
||||
"x": 70,
|
||||
"y": 70
|
||||
},
|
||||
"transform": {
|
||||
"scaleX": 0.6,
|
||||
"scaleY": 0.6
|
||||
},
|
||||
"action": {
|
||||
"type": "event",
|
||||
"data": {
|
||||
"event": "back"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"id": "slot0_bg",
|
||||
"type": "Clip",
|
||||
"assets": [
|
||||
{ "id": "slot0_selected", "src": "assets/player/textures/DropDownItemSelected.png", "type": "texture" },
|
||||
{ "id": "slot0_normal", "src": "assets/player/textures/DropDownItem.png", "type": "texture" }
|
||||
],
|
||||
"position": { "x": 319, "y": 220 }
|
||||
},
|
||||
{
|
||||
"id": "slot0_label",
|
||||
"type": "Label",
|
||||
"text": "",
|
||||
"style": { "fontSize": 38, "fontFamily": "Proxima Nova Soft", "fill": "#FFFFFF" },
|
||||
"position": { "x": "CENTER", "y": 260 },
|
||||
"targetAnchor": { "x": "0.5", "y": "0.5" }
|
||||
},
|
||||
{
|
||||
"id": "slot0_btn",
|
||||
"type": "Button",
|
||||
"position": { "x": 319, "y": 220 },
|
||||
"hitArea": { "x": 0, "y": 0, "width": 643, "height": 81 },
|
||||
"action": { "type": "event", "data": { "event": "slotPress", "index": 0 } }
|
||||
},
|
||||
|
||||
{
|
||||
"id": "slot1_bg",
|
||||
"type": "Clip",
|
||||
"assets": [
|
||||
{ "id": "slot1_selected", "src": "assets/player/textures/DropDownItemSelected.png", "type": "texture" },
|
||||
{ "id": "slot1_normal", "src": "assets/player/textures/DropDownItem.png", "type": "texture" }
|
||||
],
|
||||
"position": { "x": 319, "y": 310 }
|
||||
},
|
||||
{
|
||||
"id": "slot1_label",
|
||||
"type": "Label",
|
||||
"text": "",
|
||||
"style": { "fontSize": 38, "fontFamily": "Proxima Nova Soft", "fill": "#FFFFFF" },
|
||||
"position": { "x": "CENTER", "y": 350 },
|
||||
"targetAnchor": { "x": "0.5", "y": "0.5" }
|
||||
},
|
||||
{
|
||||
"id": "slot1_btn",
|
||||
"type": "Button",
|
||||
"position": { "x": 319, "y": 310 },
|
||||
"hitArea": { "x": 0, "y": 0, "width": 643, "height": 81 },
|
||||
"action": { "type": "event", "data": { "event": "slotPress", "index": 1 } }
|
||||
},
|
||||
|
||||
{
|
||||
"id": "slot2_bg",
|
||||
"type": "Clip",
|
||||
"assets": [
|
||||
{ "id": "slot2_selected", "src": "assets/player/textures/DropDownItemSelected.png", "type": "texture" },
|
||||
{ "id": "slot2_normal", "src": "assets/player/textures/DropDownItem.png", "type": "texture" }
|
||||
],
|
||||
"position": { "x": 319, "y": 400 }
|
||||
},
|
||||
{
|
||||
"id": "slot2_label",
|
||||
"type": "Label",
|
||||
"text": "",
|
||||
"style": { "fontSize": 38, "fontFamily": "Proxima Nova Soft", "fill": "#FFFFFF" },
|
||||
"position": { "x": "CENTER", "y": 440 },
|
||||
"targetAnchor": { "x": "0.5", "y": "0.5" }
|
||||
},
|
||||
{
|
||||
"id": "slot2_btn",
|
||||
"type": "Button",
|
||||
"position": { "x": 319, "y": 400 },
|
||||
"hitArea": { "x": 0, "y": 0, "width": 643, "height": 81 },
|
||||
"action": { "type": "event", "data": { "event": "slotPress", "index": 2 } }
|
||||
},
|
||||
|
||||
{
|
||||
"id": "slot3_bg",
|
||||
"type": "Clip",
|
||||
"assets": [
|
||||
{ "id": "slot3_selected", "src": "assets/player/textures/DropDownItemSelected.png", "type": "texture" },
|
||||
{ "id": "slot3_normal", "src": "assets/player/textures/DropDownItem.png", "type": "texture" }
|
||||
],
|
||||
"position": { "x": 319, "y": 490 }
|
||||
},
|
||||
{
|
||||
"id": "slot3_label",
|
||||
"type": "Label",
|
||||
"text": "",
|
||||
"style": { "fontSize": 38, "fontFamily": "Proxima Nova Soft", "fill": "#FFFFFF" },
|
||||
"position": { "x": "CENTER", "y": 530 },
|
||||
"targetAnchor": { "x": "0.5", "y": "0.5" }
|
||||
},
|
||||
{
|
||||
"id": "slot3_btn",
|
||||
"type": "Button",
|
||||
"position": { "x": 319, "y": 490 },
|
||||
"hitArea": { "x": 0, "y": 0, "width": 643, "height": 81 },
|
||||
"action": { "type": "event", "data": { "event": "slotPress", "index": 3 } }
|
||||
},
|
||||
|
||||
{
|
||||
"id": "slot4_bg",
|
||||
"type": "Clip",
|
||||
"assets": [
|
||||
{ "id": "slot4_selected", "src": "assets/player/textures/DropDownItemSelected.png", "type": "texture" },
|
||||
{ "id": "slot4_normal", "src": "assets/player/textures/DropDownItem.png", "type": "texture" }
|
||||
],
|
||||
"position": { "x": 319, "y": 580 }
|
||||
},
|
||||
{
|
||||
"id": "slot4_label",
|
||||
"type": "Label",
|
||||
"text": "",
|
||||
"style": { "fontSize": 38, "fontFamily": "Proxima Nova Soft", "fill": "#FFFFFF" },
|
||||
"position": { "x": "CENTER", "y": 620 },
|
||||
"targetAnchor": { "x": "0.5", "y": "0.5" }
|
||||
},
|
||||
{
|
||||
"id": "slot4_btn",
|
||||
"type": "Button",
|
||||
"position": { "x": 319, "y": 580 },
|
||||
"hitArea": { "x": 0, "y": 0, "width": 643, "height": 81 },
|
||||
"action": { "type": "event", "data": { "event": "slotPress", "index": 4 } }
|
||||
}
|
||||
]
|
||||
}
|
||||
BIN
V3.1/build/opt/jibo/Jibo/Skills/@be/be/node_modules/@be/plex-music/assets/player/textures/DropDownItem.png
generated
vendored
Normal file
BIN
V3.1/build/opt/jibo/Jibo/Skills/@be/be/node_modules/@be/plex-music/assets/player/textures/DropDownItem.png
generated
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 505 B |
BIN
V3.1/build/opt/jibo/Jibo/Skills/@be/be/node_modules/@be/plex-music/assets/player/textures/DropDownItemSelected.png
generated
vendored
Normal file
BIN
V3.1/build/opt/jibo/Jibo/Skills/@be/be/node_modules/@be/plex-music/assets/player/textures/DropDownItemSelected.png
generated
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 543 B |
BIN
V3.1/build/opt/jibo/Jibo/Skills/@be/be/node_modules/@be/plex-music/assets/player/textures/MediaServerMenu.kra
generated
vendored
Normal file
BIN
V3.1/build/opt/jibo/Jibo/Skills/@be/be/node_modules/@be/plex-music/assets/player/textures/MediaServerMenu.kra
generated
vendored
Normal file
Binary file not shown.
BIN
V3.1/build/opt/jibo/Jibo/Skills/@be/be/node_modules/@be/plex-music/assets/player/textures/MediaServerMenu.png
generated
vendored
Normal file
BIN
V3.1/build/opt/jibo/Jibo/Skills/@be/be/node_modules/@be/plex-music/assets/player/textures/MediaServerMenu.png
generated
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 16 KiB |
BIN
V3.1/build/opt/jibo/Jibo/Skills/@be/be/node_modules/@be/plex-music/assets/player/textures/MediaServerMenu.png-autosave.kra
generated
vendored
Normal file
BIN
V3.1/build/opt/jibo/Jibo/Skills/@be/be/node_modules/@be/plex-music/assets/player/textures/MediaServerMenu.png-autosave.kra
generated
vendored
Normal file
Binary file not shown.
426
V3.1/build/opt/jibo/Jibo/Skills/@be/be/node_modules/@be/plex-music/index.js
generated
vendored
426
V3.1/build/opt/jibo/Jibo/Skills/@be/be/node_modules/@be/plex-music/index.js
generated
vendored
@@ -2,6 +2,8 @@
|
||||
|
||||
const jibo = require("jibo");
|
||||
const beFramework = require("@be/be-framework");
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
|
||||
let rlog = null;
|
||||
try {
|
||||
@@ -26,15 +28,251 @@ function log(line, data) {
|
||||
class PlexMusic extends beFramework.BeSkill {
|
||||
constructor(assetPack) {
|
||||
super(assetPack);
|
||||
this._menuView = null;
|
||||
this._onSelect = this._onSelect.bind(this);
|
||||
this._view = null;
|
||||
|
||||
this._config = null;
|
||||
this._servers = [];
|
||||
this._currentServerId = null;
|
||||
this._serverPickerView = null;
|
||||
|
||||
this._onBack = this._onBack.bind(this);
|
||||
this._onServer = this._onServer.bind(this);
|
||||
this._onBrowse = this._onBrowse.bind(this);
|
||||
this._onServerPicked = this._onServerPicked.bind(this);
|
||||
this._onServerPickerCancel = this._onServerPickerCancel.bind(this);
|
||||
this._onSlotPress = this._onSlotPress.bind(this);
|
||||
this._mode = 'servers';
|
||||
this._slotItems = [];
|
||||
}
|
||||
|
||||
_configPaths() {
|
||||
const paths = [];
|
||||
try {
|
||||
if (process && process.env && process.env.JIBO_PLEX_MUSIC_CONFIG) {
|
||||
paths.push(String(process.env.JIBO_PLEX_MUSIC_CONFIG));
|
||||
}
|
||||
} catch (e) {}
|
||||
|
||||
// Preferred: shared config in Skills/@be so it survives skill updates.
|
||||
paths.push("/opt/jibo/Jibo/Skills/@be/plex-music-config.json");
|
||||
|
||||
// Fallback: local config next to this package.
|
||||
try {
|
||||
const resolved = jibo.utils.PathUtils.resolve(this.assetPack);
|
||||
const localDir = path.dirname(resolved);
|
||||
paths.push(path.join(localDir, "plex-music-config.json"));
|
||||
} catch (e) {}
|
||||
|
||||
return paths;
|
||||
}
|
||||
|
||||
_readJson(filePath) {
|
||||
try {
|
||||
const txt = fs.readFileSync(filePath, "utf8");
|
||||
if (!txt || txt.trim().length === 0) return null;
|
||||
return JSON.parse(txt);
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
_loadConfig() {
|
||||
const tried = [];
|
||||
const paths = this._configPaths();
|
||||
|
||||
for (let i = 0; i < paths.length; i++) {
|
||||
const p = paths[i];
|
||||
tried.push(p);
|
||||
const obj = this._readJson(p);
|
||||
if (obj) {
|
||||
log("loaded config", { path: p });
|
||||
return { config: obj, path: p, tried: tried };
|
||||
}
|
||||
}
|
||||
|
||||
log("no config found", { tried: tried });
|
||||
return { config: null, path: null, tried: tried };
|
||||
}
|
||||
|
||||
_normalizeServers(config) {
|
||||
const servers = (config && Array.isArray(config.servers)) ? config.servers : [];
|
||||
return servers
|
||||
.filter((s) => s && typeof s.id === "string" && s.id.trim().length)
|
||||
.map((s) => {
|
||||
return {
|
||||
id: String(s.id),
|
||||
type: String(s.type || "plex"),
|
||||
name: String(s.name || s.id),
|
||||
baseUrl: s.baseUrl ? String(s.baseUrl) : null,
|
||||
token: s.token ? String(s.token) : "",
|
||||
raw: s
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
_selectDefaultServer(config, servers) {
|
||||
if (!servers.length) return null;
|
||||
const preferred = config && config.defaultServerId ? String(config.defaultServerId) : null;
|
||||
if (preferred) {
|
||||
const match = servers.find((s) => s.id === preferred);
|
||||
if (match) return match.id;
|
||||
}
|
||||
return servers[0].id;
|
||||
}
|
||||
|
||||
_currentServer() {
|
||||
if (!this._currentServerId) return null;
|
||||
return this._servers.find((s) => s.id === this._currentServerId) || null;
|
||||
}
|
||||
|
||||
_setStatus(text) {
|
||||
try {
|
||||
const status = this._view && this._view.getComponentById ? this._view.getComponentById("status") : null;
|
||||
if (status && typeof status.setText === "function") {
|
||||
status.setText(String(text || ""));
|
||||
}
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
_refreshUiFromConfig() {
|
||||
const server = this._currentServer();
|
||||
if (!this._servers.length) {
|
||||
this._setStatus("Status: no servers configured");
|
||||
return;
|
||||
}
|
||||
if (!server) {
|
||||
this._setStatus("Status: select a server");
|
||||
this._showServers();
|
||||
return;
|
||||
}
|
||||
this._setStatus(`Server: ${server.name} (${server.type})`);
|
||||
if (this._mode === 'songs') {
|
||||
this._showSongs();
|
||||
} else {
|
||||
this._showServers();
|
||||
}
|
||||
}
|
||||
|
||||
_onSlotPress(data) {
|
||||
var slotIndex = data && typeof data.index === 'number' ? data.index : null;
|
||||
if (slotIndex === null || slotIndex < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
var item = this._slotItems[slotIndex];
|
||||
if (!item) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this._mode === 'servers') {
|
||||
this._currentServerId = item.id;
|
||||
this._mode = 'songs';
|
||||
this._refreshUiFromConfig();
|
||||
return;
|
||||
}
|
||||
|
||||
if (this._mode === 'songs') {
|
||||
try {
|
||||
var status = this._view.getComponentById('status');
|
||||
if (status && typeof status.setText === 'function') {
|
||||
status.setText('Selected: ' + (item.title || item.id));
|
||||
}
|
||||
} catch (e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_showServers() {
|
||||
this._mode = 'servers';
|
||||
var items = (this._servers || []).slice(0, 5).map(function (s) {
|
||||
return {
|
||||
kind: 'server',
|
||||
id: s.id,
|
||||
title: s.name || s.id
|
||||
};
|
||||
});
|
||||
|
||||
this._slotItems = items;
|
||||
this._renderSlots(items, { selectedId: this._currentServerId });
|
||||
}
|
||||
|
||||
_showSongs() {
|
||||
this._mode = 'songs';
|
||||
var server = this._currentServer();
|
||||
if (!server) {
|
||||
this._showServers();
|
||||
return;
|
||||
}
|
||||
|
||||
var placeholder = [
|
||||
{ kind: 'song', id: 'song-1', title: 'Placeholder Song 1' },
|
||||
{ kind: 'song', id: 'song-2', title: 'Placeholder Song 2' },
|
||||
{ kind: 'song', id: 'song-3', title: 'Placeholder Song 3' },
|
||||
{ kind: 'song', id: 'song-4', title: 'Placeholder Song 4' },
|
||||
{ kind: 'song', id: 'song-5', title: 'Placeholder Song 5' }
|
||||
];
|
||||
|
||||
this._slotItems = placeholder;
|
||||
this._renderSlots(placeholder, { selectedId: null });
|
||||
}
|
||||
|
||||
_renderSlots(items, opts) {
|
||||
if (!this._view) {
|
||||
return;
|
||||
}
|
||||
|
||||
opts = opts || {};
|
||||
for (var i = 0; i < 5; i++) {
|
||||
var item = items[i] || null;
|
||||
var labelId = 'slot' + i + '_label';
|
||||
var bgId = 'slot' + i + '_bg';
|
||||
var btnId = 'slot' + i + '_btn';
|
||||
|
||||
try {
|
||||
var label = this._view.getComponentById(labelId);
|
||||
if (label && typeof label.setText === 'function') {
|
||||
label.setText(item ? (item.title || '') : '');
|
||||
}
|
||||
if (label && label.display) {
|
||||
label.display.visible = !!item;
|
||||
}
|
||||
} catch (e) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
try {
|
||||
var bg = this._view.getComponentById(bgId);
|
||||
if (bg && bg.display) {
|
||||
bg.display.visible = !!item;
|
||||
}
|
||||
if (bg && bg.display && bg.display.children && bg.display.children.length >= 2) {
|
||||
var isSelected = !!(item && opts.selectedId && item.id === opts.selectedId);
|
||||
// In plexView.json we intentionally add the selected texture first so
|
||||
// the normal texture sits on top by default (pre-JS).
|
||||
bg.display.children[0].visible = isSelected; // selected
|
||||
bg.display.children[1].visible = !isSelected; // normal
|
||||
}
|
||||
} catch (e2) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
try {
|
||||
var btn = this._view.getComponentById(btnId);
|
||||
if (btn && btn.display) {
|
||||
btn.display.visible = !!item;
|
||||
}
|
||||
} catch (e3) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
open(result, refresh) {
|
||||
log("open", { refresh: !!refresh });
|
||||
|
||||
const changeViewOptions = {
|
||||
addView: "resources/views/menu.json",
|
||||
addView: "assets/player/plexView.json",
|
||||
transitionOpen: jibo.face.views.UP,
|
||||
transitionClose: jibo.face.views.UP
|
||||
};
|
||||
@@ -51,11 +289,25 @@ class PlexMusic extends beFramework.BeSkill {
|
||||
};
|
||||
|
||||
const onLoaded = (view) => {
|
||||
this._menuView = view || null;
|
||||
this._view = view || null;
|
||||
try {
|
||||
if (this._menuView && typeof this._menuView.on === "function") {
|
||||
this._menuView.on("select", this._onSelect);
|
||||
if (!this._view || typeof this._view.on !== "function") return;
|
||||
|
||||
this._view.on("back", this._onBack);
|
||||
this._view.on("slotPress", this._onSlotPress);
|
||||
|
||||
// Also allow system back gesture.
|
||||
this._view.once(jibo.face.views.BACK, this._onBack);
|
||||
|
||||
// Load config and update UI.
|
||||
const loaded = this._loadConfig();
|
||||
this._config = loaded.config;
|
||||
this._servers = this._normalizeServers(this._config);
|
||||
if (!this._currentServerId) {
|
||||
this._currentServerId = this._selectDefaultServer(this._config, this._servers);
|
||||
}
|
||||
this._mode = 'servers';
|
||||
this._refreshUiFromConfig();
|
||||
} catch (e) {
|
||||
log("failed to bind select handler", { err: String(e && (e.stack || e.message || e)) });
|
||||
}
|
||||
@@ -68,38 +320,164 @@ class PlexMusic extends beFramework.BeSkill {
|
||||
}
|
||||
}
|
||||
|
||||
_onSelect(selection) {
|
||||
const id = selection && (selection.id || (selection.data && selection.data.id));
|
||||
log("select", { id: id });
|
||||
_onBack() {
|
||||
log("back");
|
||||
try {
|
||||
if (this._mode === 'songs') {
|
||||
this._mode = 'servers';
|
||||
this._refreshUiFromConfig();
|
||||
return;
|
||||
}
|
||||
this.exit();
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
if (id === "back") {
|
||||
_buildServerPickerViewConfig() {
|
||||
const list = [];
|
||||
|
||||
// Cancel entry
|
||||
list.push({
|
||||
id: "__cancel__",
|
||||
label: "Cancel",
|
||||
iconSrc: "jibo://resources/actionIcons/cancel.png",
|
||||
action: {
|
||||
type: "event",
|
||||
data: { event: "cancel" }
|
||||
}
|
||||
});
|
||||
|
||||
this._servers.forEach((s) => {
|
||||
const isSelected = this._currentServerId === s.id;
|
||||
const label = (isSelected ? "✓ " : "") + s.name;
|
||||
list.push({
|
||||
id: s.id,
|
||||
label: label,
|
||||
iconSrc: "jibo://resources/actionIcons/ok.png",
|
||||
action: {
|
||||
type: "event",
|
||||
data: { event: "picked", serverId: s.id }
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return {
|
||||
viewConfig: {
|
||||
type: "MenuView",
|
||||
id: "plexServerPicker",
|
||||
title: "Select Server",
|
||||
elementsPerPage: 3,
|
||||
listDefault: {
|
||||
menuButtonType: "ActionBigButton",
|
||||
colors: ["0x58586D", "0x282735"]
|
||||
},
|
||||
list: list
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
_onServer() {
|
||||
log("server picker");
|
||||
|
||||
if (!this._servers.length) {
|
||||
this._setStatus("Status: no servers configured (edit plex-music-config.json)");
|
||||
return;
|
||||
}
|
||||
|
||||
const pickerConfig = this._buildServerPickerViewConfig();
|
||||
|
||||
const onLoaded = (pickerView) => {
|
||||
this._serverPickerView = pickerView || null;
|
||||
try {
|
||||
this.exit();
|
||||
} catch (e) {}
|
||||
return;
|
||||
}
|
||||
if (!this._serverPickerView || typeof this._serverPickerView.on !== "function") return;
|
||||
this._serverPickerView.on("picked", this._onServerPicked);
|
||||
this._serverPickerView.on("cancel", this._onServerPickerCancel);
|
||||
this._serverPickerView.once(jibo.face.views.BACK, this._onServerPickerCancel);
|
||||
} catch (e) {
|
||||
log("picker bind failed", { err: String(e && (e.stack || e.message || e)) });
|
||||
}
|
||||
};
|
||||
|
||||
// For now this is just a GUI stub; real Plex logic comes next.
|
||||
if (id === "connect") {
|
||||
log("connect placeholder");
|
||||
return;
|
||||
}
|
||||
const onFailure = (err) => {
|
||||
log("picker changeView failure", { err: String(err && (err.stack || err.message || err)) });
|
||||
};
|
||||
|
||||
if (id === "browse") {
|
||||
log("browse placeholder");
|
||||
try {
|
||||
jibo.face.views.changeView(
|
||||
{
|
||||
addView: pickerConfig,
|
||||
pause: { alpha: 0.85 },
|
||||
transitionOpen: jibo.face.views.UP,
|
||||
transitionClose: jibo.face.views.UP
|
||||
},
|
||||
null,
|
||||
onFailure,
|
||||
onLoaded
|
||||
);
|
||||
} catch (e) {
|
||||
onFailure(e);
|
||||
}
|
||||
}
|
||||
|
||||
_onServerPicked(payload) {
|
||||
const picked = payload && (payload.serverId || (payload.data && payload.data.serverId));
|
||||
if (!picked) return;
|
||||
|
||||
log("server picked", { id: picked });
|
||||
this._currentServerId = String(picked);
|
||||
this._refreshUiFromConfig();
|
||||
|
||||
try {
|
||||
jibo.face.views.changeView({ remove: true, transitionOpen: jibo.face.views.DOWN, transitionClose: jibo.face.views.DOWN });
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
_onServerPickerCancel() {
|
||||
log("server picker cancel");
|
||||
try {
|
||||
jibo.face.views.changeView({ remove: true, transitionOpen: jibo.face.views.DOWN, transitionClose: jibo.face.views.DOWN });
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
_onBrowse() {
|
||||
// Placeholder: next step will list libraries/artists/albums/tracks.
|
||||
log("browse placeholder");
|
||||
|
||||
const server = this._currentServer();
|
||||
if (!server) {
|
||||
this._setStatus("Status: pick a server first");
|
||||
return;
|
||||
}
|
||||
if (server.type === "dlna") {
|
||||
this._setStatus("Status: DLNA browsing not implemented yet");
|
||||
return;
|
||||
}
|
||||
if (server.type === "plex") {
|
||||
this._setStatus("Status: Plex browsing not implemented yet");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
this._setStatus("Status: browsing not implemented yet");
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
close(done) {
|
||||
log("close");
|
||||
|
||||
try {
|
||||
if (this._menuView && typeof this._menuView.off === "function") {
|
||||
this._menuView.off("select", this._onSelect);
|
||||
if (this._view && typeof this._view.off === "function") {
|
||||
this._view.off("back", this._onBack);
|
||||
this._view.off("slotPress", this._onSlotPress);
|
||||
}
|
||||
} catch (e) {}
|
||||
this._menuView = null;
|
||||
this._view = null;
|
||||
|
||||
try {
|
||||
if (this._serverPickerView && typeof this._serverPickerView.off === "function") {
|
||||
this._serverPickerView.off("picked", this._onServerPicked);
|
||||
this._serverPickerView.off("cancel", this._onServerPickerCancel);
|
||||
}
|
||||
} catch (e) {}
|
||||
this._serverPickerView = null;
|
||||
|
||||
const onFailure = (err) => {
|
||||
log("close changeView failure", { err: String(err && (err.stack || err.message || err)) });
|
||||
|
||||
19
V3.1/build/opt/jibo/Jibo/Skills/@be/plex-music-config.json
Normal file
19
V3.1/build/opt/jibo/Jibo/Skills/@be/plex-music-config.json
Normal file
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"version": 1,
|
||||
"defaultServerId": "plex-home",
|
||||
"servers": [
|
||||
{
|
||||
"id": "plex-home",
|
||||
"type": "plex",
|
||||
"name": "Plex (Home)",
|
||||
"baseUrl": "http://192.168.1.10:32400",
|
||||
"token": ""
|
||||
},
|
||||
{
|
||||
"id": "dlna-nas",
|
||||
"type": "dlna",
|
||||
"name": "DLNA (NAS)",
|
||||
"note": "Placeholder: DLNA browsing not implemented yet"
|
||||
}
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user