Files
archived-vdo.ninja/docs.html
2024-04-17 16:43:30 -04:00

513 lines
16 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>VDO.Ninja | The Swiss Army knife of low-latency live streaming</title>
<meta name="description" content="VDO.Ninja is the Swiss Army knife of low-latency live streaming.">
<meta name="keywords" content="vdo ninja, streaming, OBS overlay, webrtc, av1, performer, browser source, guest, video, low latency, rtmp, content creators, live streaming tools">
<meta name="author" content="VDO.Ninja">
<link rel="icon" type="image/x-icon" href="https://vdo.ninja/icons/favicon.ico">
<link rel="stylesheet" href="styles.css">
<script async defer src="https://buttons.github.io/buttons.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/marked/12.0.1/lib/marked.umd.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/marked-gfm-heading-id/3.1.3/index.umd.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/marked-base-url/1.1.3/index.umd.min.js"></script>
<meta name="sourcecode" content="https://github.com/steveseguin/vdo.ninja" />
<meta name="stance-on-war" content="Steve Seguin condemns Russia's brutal invasion of Ukraine 💙💛." />
<link rel="shortcut icon" href="data:image/x-icon;," type="image/x-icon" />
<link id="favicon1" rel="icon" type="image/png" sizes="32x32" href="https://vdo.ninja/media/favicon-32x32.png" />
<link id="favicon2" rel="icon" type="image/png" sizes="16x16" href="https://vdo.ninja/media/favicon-16x16.png" />
<link id="favicon3" rel="icon" href="https://vdo.ninja/media/favicon.ico" />
<meta property="og:title" content="VDO.Ninja | Enhance Your Live Streaming">
<meta property="og:description" content="The Swiss Army knife of low-latency live streaming">
<meta property="og:url" content="https://vdo.ninja">
<meta property="og:type" content="website">
<meta name="twitter:title" content="VDO.Ninja | Tools for Live Streamers">
<meta name="twitter:description" content="VDO.Ninja, a powerful free tool for low-latency live streaming. Discover more at VDO.Ninja.">
<style>
@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600&display=swap');
body, html {
margin: 0;
padding: 0;
font-family: 'Poppins', sans-serif;
color: #333;
background-color: #e5e5e5;
padding-bottom: 40px;
line-height: 1.6;
overflow-x: hidden;
}
header {
background: #1a1a1a; /* Darker shade for a rich appearance */
color: #fff;
padding: 20px;
text-align: center;
margin-left: 270px;
}
h1 {
margin: 0;
font-size: 2.4em;
}
p {
margin: 10px 0 0;
font-size: 1.2em;
}
#downloads {
text-align: center;
padding: 20px;
}
.download-btn {
background-color: #007bff; /* Brighter shade of blue */
color: white;
border: none;
padding: 15px 32px;
text-align: center;
text-decoration: none;
display: inline-block;
font-size: 16px;
margin: 4px 2px;
cursor: pointer;
border-radius: 5px;
transition: background-color 0.3s;
}
.download-btn:hover {
background-color: #0056b3; /* Darker blue on hover */
color: white;
}
#video {
width: 100%;
text-align: center;
padding: 20px;
max-width: calc(100% - 40px);
}
iframe {
max-width: 100%;
border: none; /* Remove border for cleaner look */
}
.section {
padding: 20px 20px 40px 20px;
background-color: #fff;
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
margin: 20px;
border-radius: 10px;
}
.faq-item h3 {
margin: 10px 0;
color: #333;
}
.faq-item p {
font-size: 1em;
color: #666;
}
footer {
background-color: #000e;
color: white;
text-align: center;
padding: 7px;
position: fixed;
bottom: 0;
width: 100vw;
}
footer p {
margin: 0;
}
a {
color: #007bff; /* Links color to match buttons */
text-decoration: none;
}
a:hover {
color: #0056b3; /* Darker blue on hover */
text-decoration: underline; /* Underline on hover for better visibility */
}
.github-btn {
background-color: #cecece;
color: black;
border: none;
padding: 12px 20px;
font-size: 16px;
border-radius: 5px;
text-decoration: none;
display: inline-block;
transition: background-color 0.3s;
margin-top: 12px;
}
.github-btn:hover {
background-color: #b0aeae;
}
#github-buttons {
text-align: center;
margin: 20px 0 10px 0;
}
.logo{
max-height: 1em;
position:relative;
top:0.1em;
}
#content {
background-color: #f8f8f8;
padding: 20px;
border-radius: 8px;
border: 1px solid #ddd;
}
#content h1, #content h2, #content h3 {
color: #333;
}
#content pre, #content code {
background-color: #eee;
border-radius: 5px;
padding: 5px;
font-family: 'Courier New', Courier, monospace;
}
#content a {
color: #007BFF;
text-decoration: none;
}
img {
max-width: 100%;
}
.sidebar {
width: 250px;
position: fixed;
left: 0;
top: 0;
bottom: 0;
background-color: #f4f4f4;
overflow-y: auto;
padding: 20px;
box-shadow: 2px 0 5px rgba(0,0,0,0.1);
}
.section {
margin-left: 270px; /* Adjusted to make room for sidebar */
padding: 20px;
}
#sidebar {
list-style: none;
padding: 0;
}
#sidebar h1 {
font-size:1.4em;
margin: auto auto;
text-align: center;
}
#sidebar li a {
display: block;
padding: 10px;
text-decoration: none;
color: #333;
border-bottom: 1px solid #ddd;
}
#sidebar li a:hover {
background-color: #ddd;
}
.nested {
display: none;
list-style-type: none; /* Removes bullet points for nested lists */
padding-left: 20px; /* Indent nested lists */
}
#sidebar li a {
display: block;
padding: 8px;
text-decoration: none;
color: #333;
cursor: pointer;
}
#sidebar li a:hover {
background-color: #ddd;
}
.header-link {
color: inherit; /* Makes the link color the same as the text color */
text-decoration: none; /* No underline */
}
.header-link:hover {
text-decoration: underline; /* Optional: underline on hover */
}
.fancy-button, {
border: 1px solid black;
margin: 2px 10px;
padding: 2px 20px;
border-radius: 10px;
background-color: #eee;
}
.oddembed {
margin: 2px 5px;
padding: 2px 10px;
</style>
</head>
<body>
<header id="header">
<h1>VDO.Ninja <img class="logo" src="https://vdo.ninja/media/old_icon.png"></h1>
<p>The Swiss Army knife of low-latency live streaming</p>
<div id="github-buttons">
<a class="github-button" href="https://github.com/steveseguin/vdo.ninja" data-size="large">
<svg viewBox="0 0 16 16" width="16" height="16" class="octicon octicon-mark-github" aria-hidden="true"><path d="M8 0c4.42 0 8 3.58 8 8a8.013 8.013 0 0 1-5.45 7.59c-.4.08-.55-.17-.55-.38 0-.27.01-1.13.01-2.2 0-.75-.25-1.23-.54-1.48 1.78-.2 3.65-.88 3.65-3.95 0-.88-.31-1.59-.82-2.15.08-.2.36-1.02-.08-2.12 0 0-.67-.22-2.2.82-.64-.18-1.32-.27-2-.27-.68 0-1.36.09-2 .27-1.53-1.03-2.2-.82-2.2-.82-.44 1.1-.16 1.92-.08 2.12-.51.56-.82 1.28-.82 2.15 0 3.06 1.86 3.75 3.64 3.95-.23.2-.44.55-.51 1.07-.46.21-1.61.55-2.33-.66-.15-.24-.6-.83-1.23-.82-.67.01-.27.38.01.53.34.19.73.9.82 1.13.16.45.68 1.31 2.69.94 0 .67.01 1.3.01 1.49 0 .21-.15.45-.55.38A7.995 7.995 0 0 1 0 8c0-4.42 3.58-8 8-8Z"></path></svg>
<span class="d-none d-sm-inline"> View on GitHub </span>
</a>
<a class="github-button" href="https://github.com/steveseguin/vdo.ninja" data-icon="octicon-star" data-size="large" data-show-count="true" aria-label="Star steveseguin/vdo.ninja on GitHub">Star</a>
<a class="github-button" href="https://github.com/steveseguin/vdo.ninja/fork" data-icon="octicon-repo-forked" data-size="large" data-show-count="true" aria-label="Fork steveseguin/vdo.ninja on GitHub">Fork</a>
<a class="github-button" href="https://github.com/steveseguin/vdo.ninja/subscription" data-icon="octicon-eye" data-size="large" data-show-count="true" aria-label="Watch steveseguin/vdo.ninja on GitHub">Watch</a>
<a class="github-button" href="https://github.com/sponsors/steveseguin" data-icon="octicon-heart" data-size="large" aria-label="Sponsor @steveseguin on GitHub">Sponsor</a>
</div>
</header>
<div id="sidebar" class="sidebar"></div>
<section id="markdown" class="section">
</section>
<footer>
<p>Join our community for free support on <a href="https://discord.vdo.ninja" target="_blank">Discord</a>.</p>
</footer>
<script>
function smoothScroll(target) {
const element = document.getElementById(target);
if (element) {
window.scrollTo({
top: element.offsetTop,
behavior: 'smooth'
});
}
}
function replaceGitbookTemplates(text) {
const embedPattern = /{% embed url="([^"]+)" %}([^{%]+){% endembed %}/g;
const contentRefPattern = /{% content-ref url="([^"]+)" %}([^{%]+){% endcontent-ref %}/g;
const hintRefPattern = /{% hint style="([^"]+)" %}([^{%]+){% endhint %}/g;
// Replace embeds
text = text.replace(embedPattern, (match, url, description) => {
let src;
const urlObj = new URL(url);
const videoId = new URLSearchParams(urlObj.search).get('v');
// Check if it's a YouTube URL and use the video ID if available
if (urlObj.hostname.includes('youtube.com') && videoId) {
src = `https://www.youtube.com/embed/${videoId}`;
var iframeHtml = ` <iframe width="560" height="315" src="${src}" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe> `;
description = marked.parse(description);
} else if (urlObj.hostname.includes('youtu.be')) {
src = `https://www.youtube.com/embed/${urlObj.pathname.slice(1)}`;
var iframeHtml = ` <iframe width="560" height="315" src="${src}" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe> `;
description = marked.parse(description);
} else {
src = url; // Use the full URL for other types of embeds
//description = marked.parse(description);
return ` <a class='oddembed' href="${src}">${description.trim()}</a> `;
}
return `<div>${iframeHtml}<br><small><i>${description.trim()}</i></small></div>`;
});
// Replace content references
text = text.replace(contentRefPattern, (match, url, text) => {
return `<span class="fancy-button">${text.trim()}</span>`;
});
// Replace content references
text = text.replace(hintRefPattern, (match, url, text) => {
return `<small class="`+url+`"><i>${text.trim()}</i></small>`;
});
return text;
}
const githubBaseURL = 'https://raw.githubusercontent.com/steveseguin/vdo.ninja/gitbook/';
function updateURL(path) {
if (history.pushState) {
const currentHash = window.location.hash; // Store current hash
const newurl = `${window.location.protocol}//${window.location.host}${window.location.pathname}?file=${encodeURIComponent(path)}${currentHash}`;
window.history.pushState({path: newurl}, '', newurl);
}
}
function makeHeaderLinks() {
document.getElementById('markdown').querySelectorAll('h1[id], h2[id], h3[id], h4[id]').forEach(header => {
if (!header.parentNode.matches('a')) { // Check if the header is not already inside a link
const id = header.getAttribute('id');
header.innerHTML = `<a href="#${id}" class="header-link">${header.innerHTML}</a>`;
}
});
if (window.location.hash) {
document.getElementById(window.location.hash.substring(1)).scrollIntoView();
}
}
function loadMarkdownFromURL() {
(function(w) {
w.URLSearchParams = w.URLSearchParams || function(searchString) {
var self = this;
searchString = searchString.replace("??", "?");
self.searchString = searchString;
self.get = function(name) {
var results = new RegExp('[\?&]' + name + '=([^&#]*)').exec(self.searchString);
if (results == null) {
return null;
} else {
return decodeURI(results[1]) || 0;
}
};
};
})(window);
var urlEdited = window.location.search.replace(/\?\?/g, "?");
urlEdited = urlEdited.replace(/\?/g, "&");
urlEdited = urlEdited.replace(/\&/, "?");
var urlParams = new URLSearchParams(urlEdited);
const file = urlParams.get('file');
if (file) {
const filePath = decodeURIComponent(file);
fetchMarkdown(githubBaseURL + filePath);
return true;
}
return false
}
function fetchMarkdown(href, e=false) {
if (href.endsWith('.md') || (href.startsWith(githubBaseURL) && href.endsWith("/"))) {
if (e){
document.getElementById('header').style.display = "none";
window.location.hash = "";
e.preventDefault();
}
if (!href.endsWith(".md") && href.endsWith("/")){
href += "README.md";
}
if (href.startsWith('http')) {
let x = href.split("/");
x.pop();
x = x.join("/")+"/";
marked.use(markedBaseUrl.baseUrl(x));
updateURL((href).replace(githubBaseURL, ''));
fetch(href)
.then(response => response.text())
.then(text => {
let html = replaceGitbookTemplates(text);
html = marked.parse(html);
document.getElementById('markdown').innerHTML = html;
makeHeaderLinks();
})
.catch(error => console.error('Error loading the markdown file:', error));
} else {
let x = href.split("/");
x.pop();
x = x.join("/")+"/";
marked.use(markedBaseUrl.baseUrl(githubBaseURL+x));
updateURL((githubBaseURL + href).replace(githubBaseURL, ''));
fetch(githubBaseURL + href) // Fetch the markdown file from GitHub
.then(response => response.text())
.then(text => {
let html = replaceGitbookTemplates(text);
html = marked.parse(html);
document.getElementById('markdown').innerHTML = html;
makeHeaderLinks();
})
.catch(error => console.error('Error loading the markdown file:', error));
}
} else if (href.startsWith('http')) {
// This is an absolute URL, let the browser handle it normally
window.open(href, '_blank');
if (e){
e.preventDefault();
}
}
}
document.addEventListener("DOMContentLoaded", function() {
marked.use(markedGfmHeadingId.gfmHeadingId({})); // fml
fetch(githubBaseURL+'SUMMARY.md')
.then(response => response.text())
.then(text => {
const html = marked.parse(text);
document.getElementById('sidebar').innerHTML = html; // Insert converted HTML to the DOM
const subMenus = document.querySelectorAll('#sidebar li ul');
subMenus.forEach(menu => {
menu.classList.add('nested');
});
const sidebarMenu = document.querySelector('#sidebar');
sidebarMenu.addEventListener('click', function(e) {
if (e.target && e.target.nodeName === "A") {
const nextUl = e.target.nextElementSibling;
if (nextUl && nextUl.tagName === 'UL') {
// Prevent default if there's a nested UL to toggle
e.preventDefault();
nextUl.style.display = (nextUl.style.display === 'none' || !nextUl.style.display) ? 'block' : 'none';
}
}
});
document.body.addEventListener('click', function(e) {
if (e.target && e.target.nodeName === "A") {
const href = e.target.getAttribute('href');
fetchMarkdown(href,e);
}
});
var saved = loadMarkdownFromURL();
if (!saved){
fetch(githubBaseURL+'README.md')
.then(response => response.text())
.then(text => {
let html = replaceGitbookTemplates(text);
html = marked.parse(html);
document.getElementById('markdown').innerHTML = html;
})
.catch(error => console.error('Error loading the README:', error));
}
})
.catch(error => console.error('Error loading the SUMMARY:', error));
});
</script>
</body>
</html>