mirror of
https://github.com/SrIzan10/vdo.ninja.git
synced 2026-05-01 11:05:24 +00:00
513 lines
16 KiB
HTML
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> |