Last active
November 7, 2023 04:36
-
-
Save sanjarcode/c8d88c86b90db060a8448cf913c7bf8c to your computer and use it in GitHub Desktop.
Revisions
-
sanjarcode revised this gist
Nov 7, 2023 . 1 changed file with 2 additions and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -19,4 +19,5 @@ XSPF files are used by VLC to store a string of videos. You can open the file wi Video courses, especially downloaded ones, like Coursera etc. I start a server on the download device via npm package [http-server](https://www.npmjs.com/package/http-server), i.e. `http-server .`. Now I can see the video on all devices on my home network. Hope VLC is [sandboxed](https://en.wikipedia.org/wiki/Sandbox_(computer_security)). -
sanjarcode revised this gist
Nov 7, 2023 . No changes.There are no files selected for viewing
-
sanjarcode revised this gist
Nov 7, 2023 . 1 changed file with 2 additions and 0 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -18,3 +18,5 @@ XSPF files are used by VLC to store a string of videos. You can open the file wi ## Useful for Video courses, especially downloaded ones, like Coursera etc. I start a server on the download device via npm package [http-server](https://www.npmjs.com/package/http-server), i.e. `http-server .`. Now I can see the video on all devices on my home network. -
sanjarcode created this gist
Oct 25, 2023 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,20 @@ ## Why I had some videos (actually a folder that one more level of folders and finally video files). I wanted to generate `.xspf` files for the whole folder, where each subfolder became a playlist. i.e I wanted to generate multiple files (one corresponding to each folder) XSPF files are used by VLC to store a string of videos. You can open the file with VLC and it will play them as a playlist, allowing prev/next video. ## How to use - `tree.js` is used for generating the tree structure of the root folder, as an object. - All leaves are .mp4 videos, values being the duration in ms. - Set the destination in the function it exports (as an arg). - Run the file `node tree.js`, then copy the output to a JSON file and run a formatter. - `file-gen` - Set the JSON file destination in the function it exports (as an arg). - Run the file `node tree.js`. XSPF files are generated. ## Useful for Video courses, especially downloaded ones, like Coursera etc. This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,120 @@ const path = require("path"); const fs = require("fs"); function generateXSPF({ title = "Getting started", children = [ { pre: "http://192.168.0.103:8080", path: "1.%20Getting%20Started", file: "2-%20Prerequisites.mp4", duration: 38615, }, ], }) { const playListStart = `<?xml version="1.0" encoding="UTF-8"?> <playlist xmlns="http://xspf.org/ns/0/" xmlns:vlc="http://www.videolan.org/vlc/playlist/ns/0/" version="1"> `; const playListEnd = `</playlist>`; const _title = () => `<title>${title}</title>`; const _track = ({ pre = "http://192.168.0.103:8080", path = "1.%20Getting%20Started", file = "2-%20Prerequisites.mp4", duration = 38615, index = 0, }) => `<track> <location>${[pre, path, file].join("/")}</location>` + // `<duration>${duration}</duration>` + ` <extension application="http://www.videolan.org/vlc/playlist/0"> <vlc:id>${index}</vlc:id> </extension> </track>`; const trackList = (arr = []) => `<trackList>${arr .map((obj, index) => _track({ ...obj, index })) .join("")}</trackList>`; const extensionTags = ( n ) => `<extension application="http://www.videolan.org/vlc/playlist/0"> ${Array(n) .fill(null) .map((_, i) => `<vlc:item tid="${i}" />`) .join("")} </extension>`; const final = [ playListStart, _title(title), trackList(children), extensionTags(children.length), playListEnd, ].join(""); return final; } /** * * @returns {[{title, fileContent}]} */ function getFileObjects(obj, pre = "http://192.168.0.103:8080") { const op = []; Object.entries(obj) .sort(([a], [b]) => a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }) ) .forEach(([key, value]) => { const title = key; const children = Object.entries(value) .sort(([a], [b]) => a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }) ) .reduce((accum, [key, value], index) => { accum.push({ pre: pre, path: encodeURIComponent(title), file: encodeURIComponent(key), duration: value, }); return accum; }, []); // if (op.length === 0) op.push({ title, fileContent: generateXSPF({ title, children }) }); }); return op; } function generateFilesFromFileObjects(pathToJSONFile = {}) { let obj = pathToJSONFile; if (typeof pathToJSONFile === typeof "") { obj = require(pathToJSONFile); } const fileObjects = getFileObjects(obj); const HIDDEN_CHARACTER = ` `; fileObjects.forEach(({ title, fileContent }) => { const friendlyTitle = title .replaceAll("'", "") .replaceAll('"', "") .replaceAll(".", "") .replaceAll(HIDDEN_CHARACTER, "") .replaceAll(" ", "-") + ".xspf"; const filePath = path.join(__dirname, friendlyTitle); fs.writeFileSync(filePath, fileContent); }); } module.exports = { generateFilesFromFileObjects }; // generateFilesFromFileObjects("./one.json"); This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,60 @@ var fs = require("fs"); var path = require("path"); var filetree = {}; const util = require("util"); const runInTerminal = util.promisify(require("child_process").exec); const limitDecimalsWithRounding = (value, maxDecimals = 2) => { const amount = parseFloat(value); const power = 10 ** maxDecimals; return Math.round(amount * power) / power; }; var walkDirectory = async function (location, obj = {}, untilNow = __dirname) { var dir = fs.readdirSync(location); for (var i = 0; i < dir.length; i++) { var name = dir[i]; var target = location + "/" + name; var stats = fs.statSync(target); if (stats.isFile()) { if (name.slice(-5).includes(".")) { // obj[name.slice(0, -3)] = require(target); const currentFilePath = path.join(untilNow, name); const isVideo = currentFilePath.endsWith(".mp4"); if (!isVideo) false; const op = !isVideo ? await runInTerminal(`du -h "${currentFilePath}"`) : await runInTerminal( `ffprobe "${currentFilePath}" -show_entries format=duration -v quiet -of csv="p=0";` ); const { stdout, stderr } = op; // console.log({ stdout, stderr }); const usefulOp = isVideo ? Math.round( 1e3 * limitDecimalsWithRounding(stdout.split("\n").at(0), 4) ) : limitDecimalsWithRounding(stdout.split("\t").at(0), 4); obj[name] = stderr ? usefulOp : null; } } else if (stats.isDirectory()) { obj[name] = {}; await walkDirectory(target, obj[name], path.join(untilNow, name)); } } }; // walkDirectory(".", filetree).then(() => console.log(filetree)); module.exports = { /** * @returns {Object} */ async getVideoFilesTreeAsObject(location = ".") { const retVal = await walkDirectory(location, {}, location); return retVal; }, };