add kodi api web interface

This commit is contained in:
Jurgis Sakalauskas
2023-01-04 09:13:06 +02:00
parent 9cf70326af
commit 8c900a774e
8 changed files with 555 additions and 8 deletions
@@ -24,7 +24,6 @@ export class LrtApiSearchClient {
const resp = await firstValueFrom(this.httpService.get(url));
const searchResponse: SearchResponseInterface = resp.data;
const cats = [];
const loadedCategories = new Set();
//todo - move to mapper
@@ -34,9 +33,6 @@ export class LrtApiSearchClient {
);
for (const searchItem of searchResponse.items) {
if (cats.indexOf(searchItem.category_title) > -1) {
continue;
}
if (loadedCategories.has(searchItem.category_title)) {
continue;
}
@@ -49,7 +45,6 @@ export class LrtApiSearchClient {
searchItem.img_path_postfix;
itemDto.categoryId = await this.findCategoryId(searchItem.category_url);
cats.push(searchItem.category_title);
loadedCategories.add(searchItem.category_title);
responseDto.addItem(itemDto);
}
@@ -69,7 +64,7 @@ export class LrtApiSearchClient {
const cat = new LrtCategory();
cat.categoryUrl = categoryUrl;
cat.categoryId = catId;
this.lrtCategoriesRepository.save(cat);
this.lrtCategoriesRepository.save(cat).then();
}
private async findCategoryIdInDb(
+291
View File
@@ -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 {}
@@ -15,6 +15,7 @@ export class StreamerService {
private readonly responseHeadersService: ResponseHeadersService,
private readonly responseSender: ResponseSenderService,
) {}
stream(
req: Request,
res: Response,
+2
View File
@@ -10,11 +10,13 @@ import { TypeOrmModule } from '@nestjs/typeorm';
import { RequestLogMiddleware } from './RequestLog/request-log.middleware';
import { TryModule } from './TryOutModule/try.module';
import { configService } from './config/config.service';
import { KodiApiInterfaceModule } from './KodiApiInterface/kodi-api-interface.module';
@Module({
imports: [
TryModule,
KodiApiModule,
KodiApiInterfaceModule,
StaticModule,
TypeOrmModule.forRoot(configService.getTypeOrmConfig()),
],