init
This commit is contained in:
commit
3b6b7abb38
24
package.json
Normal file
24
package.json
Normal file
@ -0,0 +1,24 @@
|
||||
{
|
||||
"name": "dashboard",
|
||||
"version": "1.0.0",
|
||||
"description": "Dashboard for LS Services",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"build": "webpack"
|
||||
},
|
||||
"author": "darkeye",
|
||||
"license": "ISC",
|
||||
"devDependencies": {
|
||||
"css-loader": "^6.10.0",
|
||||
"html-loader": "^5.0.0",
|
||||
"html-webpack-plugin": "^5.6.0",
|
||||
"mini-css-extract-plugin": "^2.8.1",
|
||||
"postcss-loader": "^8.1.1",
|
||||
"style-loader": "^3.3.4",
|
||||
"webpack": "^5.91.0",
|
||||
"webpack-cli": "^5.1.4",
|
||||
"webpack-stream": "^7.0.0"
|
||||
}
|
||||
}
|
||||
73
src/css/main.css
Normal file
73
src/css/main.css
Normal file
@ -0,0 +1,73 @@
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
font-family: "Open Sans";
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
html {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
body {
|
||||
position: relative;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-content: stretch;
|
||||
justify-content: space-between;
|
||||
min-width: 100vw;
|
||||
min-height: 100vh;
|
||||
background-color: var(--background);
|
||||
color: var(--on-background);
|
||||
}
|
||||
|
||||
header {
|
||||
background-color: var(--background-level-5);
|
||||
color: var(--on-surface);
|
||||
}
|
||||
|
||||
main {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
color: var(--on-surface);
|
||||
}
|
||||
|
||||
footer {
|
||||
background-color: var(--background-level-5);
|
||||
color: var(--on-surface);
|
||||
}
|
||||
|
||||
|
||||
|
||||
.top-bar {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
overflow: hidden;
|
||||
margin: 150px 30px 15px 30px;
|
||||
color: white;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.main-content{
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.side-bar{
|
||||
width: 25vw;
|
||||
background-color: blue;
|
||||
}
|
||||
|
||||
.video-overview{
|
||||
flex-grow: 1
|
||||
}
|
||||
|
||||
#ls-player{
|
||||
border: solid thin red;
|
||||
width: 100%
|
||||
}
|
||||
34
src/html/index.html
Normal file
34
src/html/index.html
Normal file
@ -0,0 +1,34 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Mediaplayer</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="app-container">
|
||||
<div id="full-page-background"></div>
|
||||
<div id="top-bar" class="top-bar">
|
||||
<div>Continue</div>
|
||||
<div>search</div>
|
||||
</div>
|
||||
<div id="main-content" class="main-content">
|
||||
<div id="side-bar" class="side-bar">left-bar</div>
|
||||
<div id="search-results" class="search-results"></div>
|
||||
<div id="video-overview" class="video-overview">
|
||||
<div id="video-title">Test Titel</div>
|
||||
<div id="video-tags"></div>
|
||||
<div id="video-description">Test Titel</div>
|
||||
<div id="video-player-container">
|
||||
<video id="ls-player"></video>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<input type="button" id="startAppButton" value="Start" />
|
||||
<video id="video" width="320" height="240" controls></video>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
27
src/js/MediaPlayer.js
Normal file
27
src/js/MediaPlayer.js
Normal file
@ -0,0 +1,27 @@
|
||||
import MediaLibraryDescriptorLoader from "./loader/MediaLibraryDescriptorLoader.js";
|
||||
|
||||
export default class Mediaplayer{
|
||||
#rootFolderHandler = null;
|
||||
#libraryDescriptor = null;
|
||||
|
||||
async start(){
|
||||
try {
|
||||
this.#rootFolderHandler = await showDirectoryPicker({"id": "mediaplayer_amin", "mode": "readwrite"});
|
||||
this.#libraryDescriptor = await MediaLibraryDescriptorLoader.loadDescriptor(this.#rootFolderHandler);
|
||||
this.#initPlayer();
|
||||
} catch (error) {
|
||||
console.log("Unable to start media player!");
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
|
||||
#initPlayer(){
|
||||
//change ui to msg dialog "loading library ..."
|
||||
this.#loadPlaylists();
|
||||
//init ui
|
||||
}
|
||||
|
||||
#loadPlaylists(){
|
||||
|
||||
}
|
||||
}
|
||||
20
src/js/descriptor/FileDescriptor.js
Normal file
20
src/js/descriptor/FileDescriptor.js
Normal file
@ -0,0 +1,20 @@
|
||||
export default class FileDescriptor {
|
||||
/**
|
||||
* @type {string}
|
||||
*/
|
||||
path = "";
|
||||
|
||||
/**
|
||||
* @type {FileSystemFileHandle}
|
||||
*/
|
||||
handle = "";
|
||||
|
||||
/**
|
||||
* @param {string} path
|
||||
* @param {FileSystemFileHandle} handle
|
||||
*/
|
||||
constructor(path, handle){
|
||||
this.path = path;
|
||||
this.handle = handle;
|
||||
}
|
||||
}
|
||||
23
src/js/descriptor/MediaLibraryDescriptor.js
Normal file
23
src/js/descriptor/MediaLibraryDescriptor.js
Normal file
@ -0,0 +1,23 @@
|
||||
import MediaProfile from "../profile/MediaProfile.js";
|
||||
|
||||
export default class MediaLibraryDescriptor {
|
||||
/**
|
||||
* @type {string}
|
||||
*/
|
||||
name = "";
|
||||
|
||||
/**
|
||||
* @type {string}
|
||||
*/
|
||||
description = "";
|
||||
|
||||
/**
|
||||
* @type {string}
|
||||
*/
|
||||
rootPath = "./";
|
||||
|
||||
/**
|
||||
* @type {MediaProfile[]}
|
||||
*/
|
||||
profiles = [];
|
||||
}
|
||||
31
src/js/descriptor/MediaPlaylistDescriptor.js
Normal file
31
src/js/descriptor/MediaPlaylistDescriptor.js
Normal file
@ -0,0 +1,31 @@
|
||||
export default class MediaPlaylistDescriptor {
|
||||
/**
|
||||
* @type {string}
|
||||
*/
|
||||
name = "";
|
||||
|
||||
/**
|
||||
* @type {string}
|
||||
*/
|
||||
description = "";
|
||||
|
||||
/**
|
||||
* @type {string | null}
|
||||
*/
|
||||
cover = null;
|
||||
|
||||
/**
|
||||
* @type {string}
|
||||
*/
|
||||
type = PlaylistType.SERIES;
|
||||
|
||||
/**
|
||||
* @type {string[]}
|
||||
*/
|
||||
tags = [];
|
||||
|
||||
/**
|
||||
* @type {Track[]}
|
||||
*/
|
||||
tracks = [];
|
||||
}
|
||||
38
src/js/index.js
Normal file
38
src/js/index.js
Normal file
@ -0,0 +1,38 @@
|
||||
import css from "../css/main.css";
|
||||
import MediaProfile from "./profile/MediaProfile.js";
|
||||
import FSUtil from "./util/FSUtil.js";
|
||||
|
||||
async function load(){
|
||||
const dir = await showDirectoryPicker({"id": "mediaplayer_amin", "mode": "readwrite"});
|
||||
await walkFileTree(dir);
|
||||
}
|
||||
|
||||
async function walkFileTree(currentDir){
|
||||
for await (const value of currentDir.values()) {
|
||||
if(value.kind == 'directory'){
|
||||
await walkFileTree(value);
|
||||
} else if(value.kind == 'file' && value.name == 'playlist.json'){
|
||||
await getVideo(currentDir, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function getVideo(dir, playlistFile){
|
||||
const file = await playlistFile.getFile();
|
||||
console.log("Playlist file:", file);
|
||||
const content = await file.text();
|
||||
const playlist = JSON.parse(content);
|
||||
console.log(playlist);
|
||||
const videoPath = playlist.tracks[0].relativePath;
|
||||
console.log(videoPath);
|
||||
|
||||
const videoHandle = await FSUtil.getFileHandle(dir, videoPath);
|
||||
console.log(videoHandle);
|
||||
playVideo(await videoHandle.getFile());
|
||||
}
|
||||
|
||||
function playVideo(file){
|
||||
document.getElementById("video").src = URL.createObjectURL(file);
|
||||
}
|
||||
|
||||
document.getElementById("startAppButton").addEventListener("click", load);
|
||||
46
src/js/loader/MediaLibraryDescriptorLoader.js
Normal file
46
src/js/loader/MediaLibraryDescriptorLoader.js
Normal file
@ -0,0 +1,46 @@
|
||||
import MediaLibraryDescriptor from "../descriptor/MediaLibraryDescriptor.js";
|
||||
import MediaProfile from "../profile/MediaProfile.js";
|
||||
|
||||
export default class MediaLibraryDescriptorLoader {
|
||||
/**
|
||||
* @param {FileSystemDirectoryHandle} rootFolderHandler
|
||||
* @returns {Promise<MediaLibraryDescriptor>}
|
||||
*/
|
||||
static async loadDescriptor(rootFolderHandler) {
|
||||
const mediaLibrary = new MediaLibraryDescriptor();
|
||||
|
||||
try {
|
||||
const mediaDescriptorHandler = await rootFolderHandler.getFileHandle("media_library.json", { create: true });
|
||||
const descriptorFile = await mediaDescriptorHandler.getFile();
|
||||
const descriptionFileContent = await descriptorFile.text();
|
||||
|
||||
if (descriptionFileContent == null || descriptionFileContent.length < 10) {
|
||||
return mediaLibrary;
|
||||
}
|
||||
|
||||
const mediaDescriptorObj = JSON.parse(descriptionFileContent);
|
||||
|
||||
if (mediaDescriptorObj != null && mediaDescriptorObj.name != null) {
|
||||
mediaLibrary.name = mediaDescriptorObj.name;
|
||||
}
|
||||
|
||||
if (mediaDescriptorObj != null && mediaDescriptorObj.description != null) {
|
||||
mediaLibrary.description = mediaDescriptorObj.description;
|
||||
}
|
||||
|
||||
if (mediaDescriptorObj != null && mediaDescriptorObj.root != null) {
|
||||
mediaLibrary.root = mediaDescriptorObj.root;
|
||||
}
|
||||
|
||||
if (mediaDescriptorObj != null && mediaDescriptorObj.profiles != null) {
|
||||
mediaDescriptorObj.profiles.forEach(d => {
|
||||
mediaLibrary.profiles.push(MediaProfile.fromJson(d));
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
return mediaLibrary;
|
||||
}
|
||||
|
||||
return mediaLibrary;
|
||||
}
|
||||
}
|
||||
46
src/js/loader/MediaPlaylistDescriptorLoader.js
Normal file
46
src/js/loader/MediaPlaylistDescriptorLoader.js
Normal file
@ -0,0 +1,46 @@
|
||||
import MediaLibraryDescriptor from "../descriptor/MediaLibraryDescriptor.js";
|
||||
import MediaProfile from "../profile/MediaProfile.js";
|
||||
|
||||
export default class MediaPlaylistDescriptorLoader {
|
||||
/**
|
||||
* @param {FileSystemDirectoryHandle} rootFolderHandler
|
||||
* @returns {Promise<MediaLibraryDescriptor>}
|
||||
*/
|
||||
static async loadDescriptors(rootFolderHandler) {
|
||||
const mediaLibrary = new MediaLibraryDescriptor();
|
||||
|
||||
try {
|
||||
const mediaDescriptorHandler = await rootFolderHandler.getFileHandle("media_library.json", { create: true });
|
||||
const descriptorFile = await mediaDescriptorHandler.getFile();
|
||||
const descriptionFileContent = await descriptorFile.text();
|
||||
|
||||
if (descriptionFileContent == null || descriptionFileContent.length < 10) {
|
||||
return mediaLibrary;
|
||||
}
|
||||
|
||||
const mediaDescriptorObj = JSON.parse(descriptionFileContent);
|
||||
|
||||
if (mediaDescriptorObj != null && mediaDescriptorObj.name != null) {
|
||||
mediaLibrary.name = mediaDescriptorObj.name;
|
||||
}
|
||||
|
||||
if (mediaDescriptorObj != null && mediaDescriptorObj.description != null) {
|
||||
mediaLibrary.description = mediaDescriptorObj.description;
|
||||
}
|
||||
|
||||
if (mediaDescriptorObj != null && mediaDescriptorObj.root != null) {
|
||||
mediaLibrary.root = mediaDescriptorObj.root;
|
||||
}
|
||||
|
||||
if (mediaDescriptorObj != null && mediaDescriptorObj.profiles != null) {
|
||||
mediaDescriptorObj.profiles.forEach(d => {
|
||||
mediaLibrary.profiles.push(MediaProfile.fromJson(d));
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
return mediaLibrary;
|
||||
}
|
||||
|
||||
return mediaLibrary;
|
||||
}
|
||||
}
|
||||
39
src/js/playlist/Playlist.js
Normal file
39
src/js/playlist/Playlist.js
Normal file
@ -0,0 +1,39 @@
|
||||
import PlaylistType from "./PlaylistType.js";
|
||||
import Track from "./Track.js";
|
||||
|
||||
export default class Playlist {
|
||||
/**
|
||||
* @type {string}
|
||||
*/
|
||||
path = "";
|
||||
|
||||
/**
|
||||
* @type {string}
|
||||
*/
|
||||
name = "";
|
||||
|
||||
/**
|
||||
* @type {string}
|
||||
*/
|
||||
description = "";
|
||||
|
||||
/**
|
||||
* @type {string | null}
|
||||
*/
|
||||
cover = null;
|
||||
|
||||
/**
|
||||
* @type {string}
|
||||
*/
|
||||
type = PlaylistType.SERIES;
|
||||
|
||||
/**
|
||||
* @type {string[]}
|
||||
*/
|
||||
tags = [];
|
||||
|
||||
/**
|
||||
* @type {Track[]}
|
||||
*/
|
||||
tracks = [];
|
||||
}
|
||||
16
src/js/playlist/PlaylistType.js
Normal file
16
src/js/playlist/PlaylistType.js
Normal file
@ -0,0 +1,16 @@
|
||||
export default class PlaylistType {
|
||||
/**
|
||||
* @type {string}
|
||||
*/
|
||||
static SERIES = "SERIES";
|
||||
|
||||
/**
|
||||
* @type {string}
|
||||
*/
|
||||
static MOVIE = "MOVIE";
|
||||
|
||||
/**
|
||||
* @type {string}
|
||||
*/
|
||||
static AUDIO = "AUDIO";
|
||||
}
|
||||
28
src/js/playlist/Track.js
Normal file
28
src/js/playlist/Track.js
Normal file
@ -0,0 +1,28 @@
|
||||
import TrackMark from "./TrackMark.js";
|
||||
|
||||
export default class Track {
|
||||
/**
|
||||
* @type {string | null}
|
||||
*/
|
||||
group = null;
|
||||
|
||||
/**
|
||||
* @type {string}
|
||||
*/
|
||||
title = "";
|
||||
|
||||
/**
|
||||
* @type {string}
|
||||
*/
|
||||
format = "";
|
||||
|
||||
/**
|
||||
* @type {string}
|
||||
*/
|
||||
path = "";
|
||||
|
||||
/**
|
||||
* @type {TrackMark[]}
|
||||
*/
|
||||
marks = [];
|
||||
}
|
||||
6
src/js/playlist/TrackMark.js
Normal file
6
src/js/playlist/TrackMark.js
Normal file
@ -0,0 +1,6 @@
|
||||
export default class TrackMark {
|
||||
start = -1;
|
||||
end = -1;
|
||||
type = "UNKNOWN";
|
||||
name = "";
|
||||
}
|
||||
55
src/js/profile/MediaProfile.js
Normal file
55
src/js/profile/MediaProfile.js
Normal file
@ -0,0 +1,55 @@
|
||||
import MediaProfileRole from "./MediaProfileRole.js";
|
||||
import MediaProfileSettings from "./MediaProfileSettings.js";
|
||||
import MediaProfileViewSettings from "./MediaProfileViewSettings.js";
|
||||
|
||||
export default class MediaProfile{
|
||||
/**
|
||||
* @type {string}
|
||||
*/
|
||||
name = "";
|
||||
|
||||
/**
|
||||
* @type {string}
|
||||
*/
|
||||
role = MediaProfileRole.ADMIN;
|
||||
|
||||
/**
|
||||
* @type {MediaProfileSettings}
|
||||
*/
|
||||
settings = new MediaProfileSettings();
|
||||
|
||||
/**
|
||||
* @type {MediaProfileViewSettings}
|
||||
*/
|
||||
playSettings = new MediaProfileViewSettings();
|
||||
|
||||
/**
|
||||
* @param {object} jsonObj
|
||||
* @returns {MediaProfile}
|
||||
*/
|
||||
static fromJson(jsonObj) {
|
||||
const profile = new MediaProfile();
|
||||
|
||||
if (jsonObj == null) {
|
||||
return profile;
|
||||
}
|
||||
|
||||
if (jsonObj.name != null) {
|
||||
profile.name = jsonObj.name;
|
||||
}
|
||||
|
||||
if (jsonObj.role != null) {
|
||||
profile.role = jsonObj.role;
|
||||
}
|
||||
|
||||
if (jsonObj.settings != null && typeof jsonObj.settings === "object") {
|
||||
profile.settings = MediaProfileSettings.fromSettingsJson(jsonObj.settings);
|
||||
}
|
||||
|
||||
if (jsonObj.playSettings != null && typeof jsonObj.playSettings === "object") {
|
||||
profile.playSettings = MediaProfileViewSettings.fromSettingsJson(jsonObj.playSettings);
|
||||
}
|
||||
|
||||
return profile;
|
||||
}
|
||||
}
|
||||
11
src/js/profile/MediaProfileRole.js
Normal file
11
src/js/profile/MediaProfileRole.js
Normal file
@ -0,0 +1,11 @@
|
||||
export default class MediaProfileRole {
|
||||
/**
|
||||
* @type {string}
|
||||
*/
|
||||
static ADMIN = "ADMIN";
|
||||
|
||||
/**
|
||||
* @type {string}
|
||||
*/
|
||||
static USER = "USER";
|
||||
}
|
||||
51
src/js/profile/MediaProfileSettings.js
Normal file
51
src/js/profile/MediaProfileSettings.js
Normal file
@ -0,0 +1,51 @@
|
||||
export default class MediaProfileSettings {
|
||||
/**
|
||||
* @type {boolean}
|
||||
*/
|
||||
skipIntro = true;
|
||||
|
||||
/**
|
||||
* @type {boolean}
|
||||
*/
|
||||
skipOutro = true;
|
||||
|
||||
/**
|
||||
* @type {boolean}
|
||||
*/
|
||||
skipRecall = true;
|
||||
|
||||
/**
|
||||
* @type {boolean}
|
||||
*/
|
||||
skipPreview = true;
|
||||
|
||||
/**
|
||||
* @param {object} jsonObj
|
||||
* @returns {MediaProfileSettings}
|
||||
*/
|
||||
static fromSettingsJson(jsonObj) {
|
||||
const settings = new MediaProfileSettings();
|
||||
|
||||
if (jsonObj == null) {
|
||||
return settings;
|
||||
}
|
||||
|
||||
if (jsonObj.skipIntro != null) {
|
||||
settings.skipIntro = jsonObj.skipIntro;
|
||||
}
|
||||
|
||||
if (jsonObj.skipOutro != null) {
|
||||
settings.skipOutro = jsonObj.skipOutro;
|
||||
}
|
||||
|
||||
if (jsonObj.skipRecall != null) {
|
||||
settings.skipRecall = jsonObj.skipRecall;
|
||||
}
|
||||
|
||||
if (jsonObj.skipPreview != null) {
|
||||
settings.skipPreview = jsonObj.skipPreview;
|
||||
}
|
||||
|
||||
return settings;
|
||||
}
|
||||
}
|
||||
51
src/js/profile/MediaProfileViewSettings.js
Normal file
51
src/js/profile/MediaProfileViewSettings.js
Normal file
@ -0,0 +1,51 @@
|
||||
export default class MediaProfileViewSettings {
|
||||
/**
|
||||
* @type {boolean}
|
||||
*/
|
||||
hasOpenPlaylist = false;
|
||||
|
||||
/**
|
||||
* @type {string}
|
||||
*/
|
||||
playlistPath = null;
|
||||
|
||||
/**
|
||||
* @type {number}
|
||||
*/
|
||||
playlistTrackNumber = 0;
|
||||
|
||||
/**
|
||||
* @type {number}
|
||||
*/
|
||||
trackPosition = 0;
|
||||
|
||||
/**
|
||||
* @param {object} jsonObj
|
||||
* @returns {MediaProfileViewSettings}
|
||||
*/
|
||||
static fromSettingsJson(jsonObj) {
|
||||
const settings = new MediaProfileViewSettings();
|
||||
|
||||
if (jsonObj == null) {
|
||||
return settings;
|
||||
}
|
||||
|
||||
if (jsonObj.hasOpenPlaylist != null) {
|
||||
settings.hasOpenPlaylist = jsonObj.hasOpenPlaylist;
|
||||
}
|
||||
|
||||
if (jsonObj.playlistPath != null) {
|
||||
settings.playlistPath = jsonObj.playlistPath;
|
||||
}
|
||||
|
||||
if (jsonObj.playlistTrackNumber != null) {
|
||||
settings.playlistTrackNumber = jsonObj.playlistTrackNumber;
|
||||
}
|
||||
|
||||
if (jsonObj.trackPosition != null) {
|
||||
settings.trackPosition = jsonObj.trackPosition;
|
||||
}
|
||||
|
||||
return settings;
|
||||
}
|
||||
}
|
||||
159
src/js/util/FSUtil.js
Normal file
159
src/js/util/FSUtil.js
Normal file
@ -0,0 +1,159 @@
|
||||
import FileDescriptor from "../descriptor/FileDescriptor.js";
|
||||
|
||||
export default class FSUtil {
|
||||
/**
|
||||
* @param {FileSystemDirectoryHandle} rootDir
|
||||
* @param {string} path
|
||||
* @returns {Promise<string | null>}
|
||||
*/
|
||||
static async getFileContent(rootDir, path) {
|
||||
const fileHandle = await FSUtil.getFileHandle(rootDir, FSUtil.fixPath(path));
|
||||
return await FSUtil.getFileHandleFileContent(fileHandle);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {FileSystemFileHandle} handle
|
||||
* @returns {Promise<string>}
|
||||
*/
|
||||
static async getFileHandleFileContent(handle) {
|
||||
if (handle == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const file = await handle.getFile();
|
||||
return await file.text();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {FileSystemDirectoryHandle} rootDir
|
||||
* @param {string} path
|
||||
* @returns {Promise<FileSystemFileHandle | null>}
|
||||
*/
|
||||
static async getFileHandle(rootDir, path) {
|
||||
let resultDir = rootDir;
|
||||
let outputFile = null;
|
||||
const pathes = FSUtil.fixPath(path).split("/");
|
||||
let nextDirName = null;
|
||||
|
||||
while (nextDirName = pathes.shift()) {
|
||||
let nextDir = null;
|
||||
|
||||
for await (const value of resultDir.values()) {
|
||||
if (value.kind == 'directory' && value.name == nextDirName) {
|
||||
nextDir = value;
|
||||
} else if (value.kind == 'file' && value.name == nextDirName) {
|
||||
outputFile = value;
|
||||
}
|
||||
}
|
||||
|
||||
if (nextDir != null) {
|
||||
resultDir = nextDir;
|
||||
}
|
||||
}
|
||||
|
||||
return outputFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {FileSystemDirectoryHandle} rootDir
|
||||
* @param {string} path
|
||||
* @returns {Promise<FileSystemDirectoryHandle | null>}
|
||||
*/
|
||||
static async getDirectoryHandle(curDir, path) {
|
||||
let resultDir = curDir;
|
||||
const pathes = FSUtil.fixPath(path).split("/");
|
||||
let nextDirName = null;
|
||||
|
||||
while (nextDirName = pathes.shift()) {
|
||||
let nextDir = null;
|
||||
|
||||
for await (const value of resultDir.values()) {
|
||||
if (value.kind == 'directory' && value.name == nextDirName) {
|
||||
nextDir = value;
|
||||
}
|
||||
}
|
||||
|
||||
if (nextDir != null) {
|
||||
resultDir = nextDir;
|
||||
}
|
||||
}
|
||||
|
||||
return resultDir;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} path
|
||||
* @returns {string}
|
||||
*/
|
||||
static fixPath(path) {
|
||||
let fixedPath = path.replaceAll("/./", "/"); //remove same folder subpathes
|
||||
fixedPath = fixedPath.replaceAll("//", "/"); //remove double slashes
|
||||
|
||||
if (fixedPath.startsWith("/")) {
|
||||
fixedPath = fixedPath.substring(1);
|
||||
}
|
||||
|
||||
const pathParts = fixedPath.split("/");
|
||||
for (let i = pathParts.length - 1; i > 0; i--) {
|
||||
if (pathParts[i] == "..") {
|
||||
pathParts.splice(i - 1, 2);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
|
||||
return pathParts.join("/");
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {FileSystemDirectoryHandle} dir
|
||||
* @param {string} fileName
|
||||
* @param {boolean} includeSubDirs
|
||||
* @returns {Promise<FileDescriptor>}
|
||||
*/
|
||||
static async findFile(dir, fileName, includeSubDirs = true) {
|
||||
const files = await FSUtil.#findFilesInTree(dir, fileName, includeSubDirs, true);
|
||||
return files.length > 0 ? files[0] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {FileSystemDirectoryHandle} dir
|
||||
* @param {string} fileName
|
||||
* @param {boolean} includeSubDirs
|
||||
* @returns {Promise<FileDescriptor[]>}
|
||||
*/
|
||||
static async findFiles(dir, fileName, includeSubDirs = true) {
|
||||
return FSUtil.#findFilesInTree(dir, fileName, includeSubDirs, false);;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {FileSystemDirectoryHandle} dir
|
||||
* @param {string} fileName
|
||||
* @param {boolean} includeSubDirs
|
||||
* @param {boolean} stopOnFirst
|
||||
* @returns {Promise<FileDescriptor[]>}
|
||||
*/
|
||||
static async #findFilesInTree(dir, fileName, includeSubDirs = true, stopOnFirst = false, path = "") {
|
||||
const files = [];
|
||||
const subdirs = [];
|
||||
|
||||
for await (const child of dir.values()) {
|
||||
if (child.kind == 'directory' && includeSubDirs) {
|
||||
subdirs.push(child);
|
||||
} else if (child.kind == 'file' && child.name == fileName) {
|
||||
files.push(new FileDescriptor(path, child));
|
||||
if(stopOnFirst){
|
||||
return files;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (let i = 0; i < subdirs.length; i++) {
|
||||
files.push(...(await FSUtil.#findFilesInTree(subdirs[i], fileName, includeSubDirs, stopOnFirst, path + "/" + subdirs[i].name)));
|
||||
if(stopOnFirst && files.length > 0){
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return files;
|
||||
}
|
||||
}
|
||||
59
webpack.config.js
Normal file
59
webpack.config.js
Normal file
@ -0,0 +1,59 @@
|
||||
import path from 'path';
|
||||
import HtmlWebpackPlugin from 'html-webpack-plugin';
|
||||
import MiniCssExtractPlugin from 'mini-css-extract-plugin';
|
||||
import { fileURLToPath } from 'url';
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
|
||||
export default {
|
||||
entry: './src/js/index.js',
|
||||
mode: 'development',
|
||||
devtool: 'inline-source-map',
|
||||
output: {
|
||||
filename: 'main.js',
|
||||
path: path.resolve(__dirname, 'target'),
|
||||
clean: true,
|
||||
},
|
||||
plugins: [
|
||||
new HtmlWebpackPlugin({
|
||||
title: 'Mediaplayer',
|
||||
template: './src/html/index.html'
|
||||
}),
|
||||
new MiniCssExtractPlugin(),
|
||||
],
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.css$/i,
|
||||
oneOf: [
|
||||
{
|
||||
assert: { type: "css" },
|
||||
loader: "css-loader",
|
||||
options: {
|
||||
exportType: "css-style-sheet",
|
||||
}
|
||||
},
|
||||
{
|
||||
assert: { type: "text" },
|
||||
loader: "css-loader",
|
||||
options: {
|
||||
exportType: "string",
|
||||
}
|
||||
},
|
||||
{
|
||||
use: [MiniCssExtractPlugin.loader, "css-loader", "postcss-loader"]
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
test: /\.(png|jpe?g|gif|svg|eot|ttf|woff|woff2)$/i,
|
||||
type: "asset",
|
||||
},
|
||||
{
|
||||
test: /\.html$/i,
|
||||
loader: "html-loader",
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
Loading…
Reference in New Issue
Block a user