mirror of
https://github.com/sakaljurgis/kodi-api-server.git
synced 2026-07-08 20:37:41 +00:00
add kodi api web interface
This commit is contained in:
@@ -0,0 +1,291 @@
|
||||
<html lang="lt">
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet"
|
||||
integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.5.0/font/bootstrap-icons.css">
|
||||
<title>kodi api test interface</title>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<!-- Modal -->
|
||||
<div class="modal fade" id="modal" tabindex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="modalLabel">OK</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div id="modalBody" class="modal-body">
|
||||
...
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">OK</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
<h2>Welcome</h2>
|
||||
<input class="form-check-input" type="checkbox" value="" id="chkDebug">
|
||||
<label class="form-check-label" for="chkDebug">
|
||||
Debug
|
||||
</label>
|
||||
<div class="input-group mb-3">
|
||||
<label for="endpoint"></label><input type="text" class="form-control" value="/../api" id="endpoint">
|
||||
<button class="btn btn-outline-secondary" type="button" id="ok" onClick="okClicked()">OK</button>
|
||||
</div>
|
||||
<label for="path"></label><input type="text" class="form-control" id="path">
|
||||
</div>
|
||||
<div class="container">
|
||||
<strong>Category:</strong> <span id="cat"></span>
|
||||
<strong>Content:</strong> <span id="cont"></span>
|
||||
</div>
|
||||
<div class="container" id="content">
|
||||
<div class="list-group" id="list">
|
||||
<button type="button" class="list-group-item list-group-item-action" disabled>Nothing here</button>
|
||||
</div>
|
||||
<div id="video"></div>
|
||||
</div>
|
||||
<div id="debug">
|
||||
|
||||
</div>
|
||||
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js"
|
||||
integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM"
|
||||
crossorigin="anonymous"></script>
|
||||
<script>
|
||||
|
||||
let paths = [""];
|
||||
|
||||
function okClicked() {
|
||||
let path = document.getElementById("endpoint").value;
|
||||
paths = [""];
|
||||
loadApiPath(path);
|
||||
}
|
||||
|
||||
function loadApiPath(path) {
|
||||
$.ajax({
|
||||
url: path,
|
||||
success: function(result) {
|
||||
$("#path").val(path);
|
||||
if ($("#chkDebug")[0].checked) {
|
||||
$("#debug").html("<pre>" + JSON.stringify(result, null, " ") + "</pre>");
|
||||
} else {
|
||||
$("#debug").html("");
|
||||
}
|
||||
processApiResult(result);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function processApiResult(result) {
|
||||
if (!result) return; //todo error
|
||||
|
||||
if (result.msgBoxOK) {
|
||||
$("#modalBody").text(result.msgBoxOK);
|
||||
$("#modal").modal("show");
|
||||
}
|
||||
|
||||
if (result.notification) {
|
||||
$("#modalBody").text(result.notification);
|
||||
$("#modal").modal("show");
|
||||
}
|
||||
|
||||
if (result.play) {
|
||||
//play
|
||||
console.log("play, got from api " + result.play);
|
||||
genPlayer(result.play);
|
||||
$("#path").val(result.play);
|
||||
return;
|
||||
}
|
||||
|
||||
if (result.updateList) {
|
||||
updateList(result);
|
||||
return;
|
||||
}
|
||||
|
||||
if (result.content) {
|
||||
$("#cont").text(result.content);
|
||||
}
|
||||
|
||||
if (result.category) {
|
||||
$("#cat").text(result.category);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function updateList(result) {
|
||||
let contentElement = $("#cont");
|
||||
|
||||
contentElement.text("");
|
||||
if (result.content) {
|
||||
contentElement.text(result.content);
|
||||
}
|
||||
|
||||
let titleElement = $("#cat");
|
||||
titleElement.text("");
|
||||
if (result.category) {
|
||||
titleElement.text(result.category);
|
||||
}
|
||||
|
||||
if (result.items) {
|
||||
|
||||
if (!result.nosort) {
|
||||
result.items.sort(function(a, b) {
|
||||
let lbA = a.label;
|
||||
let lbB = b.label;
|
||||
|
||||
let the1 = new RegExp("(the.)", "gi");
|
||||
let the2 = new RegExp("(the)", "gi");
|
||||
lbA = lbA.replace(the1, "");
|
||||
lbA = lbA.replace(the2, "");
|
||||
lbB = lbB.replace(the1, "");
|
||||
lbB = lbB.replace(the2, "");
|
||||
|
||||
return lbA > lbB ? 1 : -1;
|
||||
});
|
||||
}
|
||||
|
||||
let itemsHtml = "<div class=\"list-group\" id=\"list\">";
|
||||
let itemIcon = "";
|
||||
if (paths.length > 1) {
|
||||
|
||||
let itemHtml = "<button type=\"button\" class=\"list-group-item list-group-item-action\"";
|
||||
itemHtml = itemHtml + " data-path=\"..\"";
|
||||
itemHtml = itemHtml + " data-folder=\"true\"";
|
||||
itemHtml = itemHtml + ">";
|
||||
|
||||
itemIcon = "<i class=\"bi-folder\"></i>";
|
||||
|
||||
itemHtml = itemHtml + itemIcon + " ..</button>";
|
||||
itemsHtml = itemsHtml + itemHtml;
|
||||
|
||||
}
|
||||
|
||||
for (let i = 0; i < result.items.length; i++) {
|
||||
let item = result.items[i];
|
||||
let itemHtml = "<button type=\"button\" class=\"list-group-item list-group-item-action\"";
|
||||
itemHtml = itemHtml + " data-path=\"" + item.path + "\"";
|
||||
if (item.searchFor) {
|
||||
itemHtml = itemHtml + " data-searchfor=\"" + item.searchFor + "\"";
|
||||
}
|
||||
itemHtml = itemHtml + " data-playable=\"" + (item.IsPlayable || item.mediatype === "video") + "\"";
|
||||
itemHtml = itemHtml + " data-folder=\"" + (item.isFolder) + "\"";
|
||||
|
||||
itemIcon = item.isFolder ? "<i class=\"bi-folder\"></i>" : "<i class=\"bi-film\"></i>";
|
||||
if (item.action) {
|
||||
if (!item.IsPlayable) {
|
||||
itemIcon = item.action === "search" ? "<i class=\"bi-search\"></i>" : "<i class=\"bi-gear\"></i>";
|
||||
}
|
||||
itemHtml = itemHtml + " data-action=\"" + item.action + "\"";
|
||||
}
|
||||
|
||||
itemHtml = itemHtml + ">";
|
||||
|
||||
itemHtml = itemHtml + itemIcon + " " + item.label + (item.plot ? "<div class=\"fw-lighter\">" + item.plot + "</div>" : "") + "</button>";
|
||||
itemsHtml = itemsHtml + itemHtml;
|
||||
}
|
||||
itemsHtml = itemsHtml + "</div>";
|
||||
$("#content").html(itemsHtml);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$(document).click("button", function(e) {
|
||||
//todo - disable navigation after click
|
||||
|
||||
let path = $(e.target).data("path");
|
||||
let folder = $(e.target).data("folder");
|
||||
let playable = $(e.target).data("playable");
|
||||
let action = $(e.target).data("action");
|
||||
let searchFor = $(e.target).data("searchfor");
|
||||
let endpoint = document.getElementById("endpoint").value;
|
||||
|
||||
if (folder && path) {
|
||||
//navigate?
|
||||
if (path === ".." || path === ".") {
|
||||
if (paths.length > 1 && path === "..") {
|
||||
paths.pop();
|
||||
}
|
||||
|
||||
path = paths[paths.length - 1];
|
||||
|
||||
} else {
|
||||
paths.push(path);
|
||||
}
|
||||
|
||||
loadApiPath(endpoint + "?path=" + path);
|
||||
return;
|
||||
}
|
||||
|
||||
if (playable && path) {
|
||||
if (action && action === "query") {
|
||||
//get path from api
|
||||
console.log("play, get path from api " + path);
|
||||
loadApiPath(endpoint + "?path=" + path);
|
||||
return;
|
||||
}
|
||||
//if http included - play directly, if not - play from server
|
||||
console.log("play " + path);
|
||||
genPlayer(path);
|
||||
return;
|
||||
}
|
||||
|
||||
if (action && action === "search") {
|
||||
let iSearchFor = searchFor ? searchFor : "";
|
||||
let searchText = prompt("What are you searching for?", iSearchFor);
|
||||
if (searchText) {
|
||||
paths.push(path + "&search=" + searchText);
|
||||
loadApiPath(endpoint + "?path=" + path + "&search=" + searchText);
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
let videoPlayer;
|
||||
|
||||
function genPlayer(urlPath) {
|
||||
|
||||
let itemsHtml = "<div class=\"list-group\" id=\"list\">";
|
||||
let itemHtml = "<button type=\"button\" class=\"list-group-item list-group-item-action\"";
|
||||
itemHtml = itemHtml + " data-path=\".\"";
|
||||
itemHtml = itemHtml + " data-folder=\"true\"";
|
||||
itemHtml = itemHtml + ">";
|
||||
|
||||
let itemIcon = "<i class=\"bi-folder\"></i>";
|
||||
|
||||
itemHtml = itemHtml + itemIcon + " Back</button>";
|
||||
itemsHtml = itemsHtml + itemHtml;
|
||||
itemsHtml = itemsHtml + "</div>";
|
||||
|
||||
$("#path").val("http://sklk.lt:8181" + urlPath);
|
||||
|
||||
let contentElement = $("#content");
|
||||
|
||||
// if (type) {
|
||||
// contentElement.html(itemsHtml + "<video width=\"100%\" controls><source src=\"" + urlPath + "\" type=\"" + type + "\"></video>");
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
contentElement.html(itemsHtml + "<video width=\"100%\" controls><source src=\"" + urlPath + "\"></video>");
|
||||
|
||||
|
||||
|
||||
// $.ajax(urlPath, {
|
||||
// method: 'HEAD',
|
||||
// success: (a, b, c) => {
|
||||
// let contentType = c.getResponseHeader('content-type')
|
||||
// if (contentType) {
|
||||
// let videoElementHtml = "<video width=\"100%\" controls><source src=\"" + urlPath + "\" type=\"" + contentType + "\"></video>";
|
||||
// contentElement.html(itemsHtml + videoElementHtml);
|
||||
// } else {
|
||||
// contentElement.html(itemsHtml + "<video width=\"100%\" controls><source src=\"" + urlPath + "\"></video>");
|
||||
// }
|
||||
// }
|
||||
// })
|
||||
}
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,14 @@
|
||||
import { Controller, Get, Header, Res } from '@nestjs/common';
|
||||
import { join } from 'path';
|
||||
import { createReadStream } from 'fs';
|
||||
import { Response } from 'express';
|
||||
|
||||
@Controller('if')
|
||||
export class KodiApiInterfaceController {
|
||||
@Get()
|
||||
@Header('content-type', 'text/html')
|
||||
async main(@Res() res: Response) {
|
||||
const readable = createReadStream(join(__dirname, './View/if.html'));
|
||||
readable.pipe(res);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { KodiApiInterfaceController } from './kodi-api-interface.controller';
|
||||
|
||||
/**
|
||||
* Playground module for playing around
|
||||
*/
|
||||
@Module({
|
||||
imports: [],
|
||||
controllers: [KodiApiInterfaceController],
|
||||
providers: [],
|
||||
})
|
||||
export class KodiApiInterfaceModule {}
|
||||
Reference in New Issue
Block a user