generated from ztimson/template
Compare commits
17
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
daaa53790a | ||
|
|
a2afcfa7bd | ||
|
|
b9ebb149ef | ||
|
|
cfcbccf152 | ||
|
|
9c8c108da0 | ||
|
|
d54d318166 | ||
|
|
4e70c78a7a | ||
|
|
fd6e2b07dc | ||
|
|
240119ee5f | ||
|
|
4817089056 | ||
|
|
169ffad863 | ||
|
|
4bfecdeacc | ||
|
|
c84008cf5c | ||
|
|
e24ad685fe | ||
|
|
e08bc14634 | ||
|
|
063a228134 | ||
|
|
15e0003958 |
@@ -1,96 +1,75 @@
|
||||
<!-- Header -->
|
||||
|
||||
<div id="top" align="center">
|
||||
<br />
|
||||
|
||||
<!-- Logo -->
|
||||
<img src="https://git.zakscode.com/repo-avatars/2b4ee6ba1f2e2618bf7694e4a52fb56d1d0ea6abafa2dcbe496ab786b86d5a76" alt="Logo" width="200" height="200">
|
||||
|
||||
<img src="https://git.zakscode.com/repo-avatars/8be45ee4b7c1acd45c73cfc7e9c189f139d69a1c9ec9aba29849ae6a8a0cfb40" alt="Logo" width="200" height="200">
|
||||
|
||||
<!-- Title -->
|
||||
### Template
|
||||
|
||||
### Homecast
|
||||
|
||||
<!-- Description -->
|
||||
Simple repository template
|
||||
|
||||
A homebrew Chromecast
|
||||
|
||||
<!-- Repo badges -->
|
||||
[](https://git.zakscode.com/ztimson/template/tags)
|
||||
[](https://git.zakscode.com/ztimson/template/pulls)
|
||||
[](https://git.zakscode.com/ztimson/template/issues)
|
||||
|
||||
[](https://git.zakscode.com/ztimson/homecast/tags)
|
||||
[](https://git.zakscode.com/ztimson/homecast/pulls)
|
||||
[](https://git.zakscode.com/ztimson/homecast/issues)
|
||||
|
||||
<!-- Links -->
|
||||
|
||||
---
|
||||
|
||||
<div>
|
||||
<a href="https://git.zakscode.com/ztimson/template/wiki" target="_blank">Documentation</a>
|
||||
• <a href="https://git.zakscode.com/ztimson/template/releases" target="_blank">Release Notes</a>
|
||||
• <a href="https://git.zakscode.com/ztimson/template/issues/new?template=.github%2fissue_template%2fbug.md" target="_blank">Report a Bug</a>
|
||||
• <a href="https://git.zakscode.com/ztimson/template/issues/new?template=.github%2fissue_template%2fenhancement.md" target="_blank">Request a Feature</a>
|
||||
<a href="https://git.zakscode.com/ztimson/homecast/wiki" target="_blank">Documentation</a>
|
||||
• <a href="https://git.zakscode.com/ztimson/homecast/releases" target="_blank">Release Notes</a>
|
||||
• <a href="https://git.zakscode.com/ztimson/homecast/issues/new?template=.github%2fissue_template%2fbug.md" target="_blank">Report a Bug</a>
|
||||
• <a href="https://git.zakscode.com/ztimson/homecast/issues/new?template=.github%2fissue_template%2fenhancement.md" target="_blank">Request a Feature</a>
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
</div>
|
||||
|
||||
## Table of Contents
|
||||
- [Template](#top)
|
||||
- [About](#about)
|
||||
- [Demo](#demo)
|
||||
- [Built With](#built-with)
|
||||
- [Setup](#setup)
|
||||
- [Production](#production)
|
||||
- [Development](#development)
|
||||
- [License](#license)
|
||||
|
||||
* [Homecast](#top)
|
||||
* [About](#about)
|
||||
* [Built With](#built-with)
|
||||
* [Setup](#setup)
|
||||
* [Production](#production)
|
||||
* [Development](#development)
|
||||
* [License](#license)
|
||||
|
||||
## About
|
||||
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
|
||||
Homecast is a homebrew, Chromecast-style streaming appliance built around a Raspberry Pi connected to a television.
|
||||
|
||||
### Demo
|
||||
The goal is to provide a simple, self-hosted casting and media experience while retaining complete control over the underlying hardware and software.
|
||||
|
||||
Website: https://git.zakscode.com
|
||||
Features include:
|
||||
- Installs on-top of any Raspberry Pi OS + Wayland
|
||||
- Allows you to fully customize and use as your own media center
|
||||
- Remote desktop into the pi via the browser on any device
|
||||
- Mirrors to the TV
|
||||
- Install the PWA via the browser to get the remote control app
|
||||
- Use the share button built into any site / app to cast media to Homecast
|
||||
- Custom screen savers, automatic background music, and AI harness integration
|
||||
|
||||
### Built With
|
||||
[](https://angular.io/)
|
||||
[](https://www.android.com/)
|
||||
[](https://www.arduino.cc/)
|
||||
[](https://getbootstrap.com)
|
||||
[](https://en.cppreference.com/w/c/language)
|
||||
[](https://cplusplus.com/)
|
||||
[](https://dotnet.microsoft.com/)
|
||||
[](https://www.w3.org/Style/CSS/Overview.en.html)
|
||||
[](https://www.djangoproject.com/)
|
||||
[](https://docker.com/)
|
||||
[](https://www.electronjs.org/)
|
||||
[](https://firebase.google.com/)
|
||||
[](https://go.dev/)
|
||||
[](https://graphql.org/)
|
||||
[](https://developer.mozilla.org/en-US/docs/Glossary/HTML)
|
||||
[](https://java.com/)
|
||||
[](https://javascript.com/)
|
||||
[](https://jquery.com )
|
||||
[](https://laravel.com)
|
||||
[](https://www.linux.org/)
|
||||
[](https://git.zakscode.com/ztimson/momentum)
|
||||
[](https://www.mongodb.com/)
|
||||
[](https://www.mysql.com/)
|
||||
[](https://nestjs.com/)
|
||||
[](https://dotnet.microsoft.com/)
|
||||
[](https://nextjs.org/)
|
||||
[](https://www.nginx.com/)
|
||||
[](https://nodejs.org/)
|
||||
[](https://p5js.org/)
|
||||
[](https://www.php.net/)
|
||||
[](https://www.postgresql.org/)
|
||||
[](https://www.python.org/)
|
||||
[](https://reactjs.org/)
|
||||
[](https://redis.com/)
|
||||
[](https://sass-lang.com/)
|
||||
[](https://en.wikipedia.org/wiki/Shell_script)
|
||||
[](https://www.microsoft.com/en-ca/sql-server)
|
||||
[](https://www.sqlite.org/index.html)
|
||||
[](https://svelte.dev/)
|
||||
[](https://typescriptlang.org/)
|
||||
[](https://microsoft.com/windows)
|
||||
[](https://vitejs.dev/)
|
||||
[](https://vuejs.org/)
|
||||
|
||||
[](https://www.linux.org/)
|
||||
[](https://nodejs.org/)
|
||||
[](https://javascript.com/)
|
||||
[](https://developer.mozilla.org/en-US/docs/Glossary/HTML)
|
||||
[](https://www.w3.org/Style/CSS/Overview.en.html)
|
||||
[](https://www.raspberrypi.com/)
|
||||
[](https://wayland.freedesktop.org/)
|
||||
|
||||
## Setup
|
||||
|
||||
@@ -102,11 +81,14 @@ Website: https://git.zakscode.com
|
||||
</summary>
|
||||
|
||||
#### Prerequisites
|
||||
- [Docker](https://docs.docker.com/install/)
|
||||
|
||||
- Raspberry Pi 4/5 (2GB min/4GB ideal)
|
||||
- Updated version of Raspberry Pi OS
|
||||
|
||||
#### Instructions
|
||||
1. Run the docker image: `docker run -p 80:80 git.zakscode.com/ztimson/template:latest`
|
||||
2. Open [http://localhost](http://localhost)
|
||||
|
||||
1. Install homecast: `curl -sL https://git.zakscode.com/ztimson/homecast/raw/branch/master/install.sh | sudo bash`
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
@@ -117,16 +99,17 @@ Website: https://git.zakscode.com
|
||||
</summary>
|
||||
|
||||
#### Prerequisites
|
||||
- [Node.js](https://nodejs.org/en/download)
|
||||
|
||||
* Node.js
|
||||
|
||||
#### Instructions
|
||||
1. Install the dependencies: `npm install`
|
||||
2. Start the Angular server: `npm run start`
|
||||
3. Open [http://localhost:4200](http://localhost:4200)
|
||||
|
||||
1. Clone the repository.
|
||||
2. Install the dependencies: `npm install`
|
||||
3. Start the development server: `npm run start`
|
||||
|
||||
</details>
|
||||
|
||||
## License
|
||||
Copyright © 2023 Zakary Timson | All Rights Reserved | Available under MIT Licensing
|
||||
|
||||
See the [license](./LICENSE) for more information.
|
||||
Copyright © 2026 Zakary Timson | Available under MIT Licensing
|
||||
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
# Fallback AP
|
||||
curl -sL https://git.zakscode.com/ztimson/fallback-ap/raw/branch/master/install.sh | sudo bash
|
||||
|
||||
# VNC
|
||||
sudo raspi-config nonint do_vnc 0
|
||||
|
||||
# Node
|
||||
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.7/install.sh | bash
|
||||
source ~/.bashrc
|
||||
nvm install stable && nvm use stable
|
||||
|
||||
# Install Homecast
|
||||
mkdir /app && cd /app
|
||||
git clone https://git.zakscode.com/ztimson/homecast.git .
|
||||
npm i
|
||||
mkdir -p certs
|
||||
openssl req -x509 -newkey rsa:2048 \
|
||||
-keyout certs/key.pem \
|
||||
-out certs/cert.pem \
|
||||
-days 3650 \
|
||||
-nodes \
|
||||
-subj "/CN=homecast"
|
||||
|
||||
# TODO: Create service
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "homecast",
|
||||
"version": "0.1.0",
|
||||
"description": "The Homebrew Chromecast",
|
||||
"main": "src/server.mjs",
|
||||
"scripts": {
|
||||
"start": "node src/server.mjs"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
"@novnc/novnc": "^1.7.0",
|
||||
"ws": "^8.21.3"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Remote Desktop</title>
|
||||
<style>
|
||||
html, body {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
overflow: hidden;
|
||||
background: #000;
|
||||
}
|
||||
|
||||
#screen {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="screen"></div>
|
||||
|
||||
<script type="module">
|
||||
import RFB from '/novnc/core/rfb.js';
|
||||
|
||||
const screen = document.querySelector('#screen');
|
||||
|
||||
const rfb = new RFB(
|
||||
screen,
|
||||
`wss://${location.host}/websockify`
|
||||
);
|
||||
|
||||
rfb.addEventListener('connect', () => console.log('RFB connected'));
|
||||
rfb.addEventListener('disconnect', event => console.log('RFB disconnected', event.detail));
|
||||
|
||||
rfb.scaleViewport = true;
|
||||
rfb.resizeSession = false;
|
||||
rfb.showDotCursor = true;
|
||||
|
||||
rfb.addEventListener('connect', () => console.log('Connected'));
|
||||
rfb.addEventListener('disconnect', event => console.log('Disconnected', event.detail));
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 5.1 KiB |
+129
@@ -0,0 +1,129 @@
|
||||
import http from 'node:http';
|
||||
import https from 'node:https';
|
||||
import net from 'node:net';
|
||||
import fs from 'node:fs';
|
||||
import {readFile} from 'node:fs/promises';
|
||||
import path from 'node:path';
|
||||
import {fileURLToPath} from 'node:url';
|
||||
import {WebSocketServer} from 'ws';
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
const NOVNC = path.join(__dirname, '../node_modules/@novnc/novnc');
|
||||
const PORT = process.env['PORT'] || 3000;
|
||||
const VNC_HOST = '127.0.0.1';
|
||||
const VNC_PORT = 5900;
|
||||
|
||||
const MIME_TYPES = {
|
||||
'.html': 'text/html; charset=utf-8',
|
||||
'.js': 'text/javascript',
|
||||
'.css': 'text/css',
|
||||
'.json': 'application/json',
|
||||
'.svg': 'image/svg+xml',
|
||||
'.png': 'image/png',
|
||||
'.ico': 'image/x-icon',
|
||||
'.wasm': 'application/wasm'
|
||||
};
|
||||
|
||||
const server = https.createServer({
|
||||
key: fs.readFileSync('./certs/key.pem'),
|
||||
cert: fs.readFileSync('./certs/cert.pem')
|
||||
}, async (req, res) => {
|
||||
let file;
|
||||
|
||||
if (req.url === '/') file = path.join(__dirname, '../public/index.html');
|
||||
else if (req.url.startsWith('/novnc/')) file = path.join(NOVNC, req.url.slice(7));
|
||||
else {
|
||||
res.writeHead(404);
|
||||
res.end('Not found');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const data = await readFile(file);
|
||||
const type = MIME_TYPES[path.extname(file)] || 'application/octet-stream';
|
||||
|
||||
res.writeHead(200, {'Content-Type': type});
|
||||
res.end(data);
|
||||
} catch (error) {
|
||||
console.error('HTTP error:', error.message);
|
||||
res.writeHead(404);
|
||||
res.end('Not found');
|
||||
}
|
||||
});
|
||||
|
||||
const wss = new WebSocketServer({
|
||||
server,
|
||||
path: '/websockify',
|
||||
handleProtocols: protocols => {
|
||||
console.log('WebSocket protocols:', [...protocols]);
|
||||
|
||||
if (protocols.has('binary')) return 'binary';
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
wss.on('connection', ws => {
|
||||
console.log('VNC client connected');
|
||||
|
||||
const socket = net.createConnection(VNC_PORT, VNC_HOST);
|
||||
|
||||
socket.on('connect', () => {
|
||||
console.log(`Connected to VNC ${VNC_HOST}:${VNC_PORT}`);
|
||||
});
|
||||
|
||||
socket.on('data', data => {
|
||||
console.log(`VNC -> browser: ${data.length} bytes`);
|
||||
|
||||
if (data.length <= 64) {
|
||||
console.log(` hex: ${data.toString('hex')}`);
|
||||
console.log(` text: ${JSON.stringify(data.toString())}`);
|
||||
}
|
||||
|
||||
if (ws.readyState === ws.OPEN) {
|
||||
ws.send(data, {binary: true});
|
||||
}
|
||||
});
|
||||
|
||||
ws.on('message', (data, isBinary) => {
|
||||
const buffer = Buffer.isBuffer(data) ? data : Buffer.from(data);
|
||||
|
||||
console.log(`Browser -> VNC: ${buffer.length} bytes (${isBinary ? 'binary' : 'text'})`);
|
||||
|
||||
if (buffer.length <= 64) {
|
||||
console.log(` hex: ${buffer.toString('hex')}`);
|
||||
console.log(` text: ${JSON.stringify(buffer.toString())}`);
|
||||
}
|
||||
|
||||
if (socket.writable) {
|
||||
socket.write(buffer);
|
||||
}
|
||||
});
|
||||
|
||||
ws.on('error', error => {
|
||||
console.error('WebSocket error:', error.message);
|
||||
socket.destroy();
|
||||
});
|
||||
|
||||
ws.on('close', (code, reason) => {
|
||||
console.log(`Browser disconnected: ${code}${reason?.length ? ` ${reason}` : ''}`);
|
||||
socket.destroy();
|
||||
});
|
||||
|
||||
socket.on('error', error => {
|
||||
console.error('VNC socket error:', error.message);
|
||||
if (ws.readyState === ws.OPEN) ws.close();
|
||||
});
|
||||
|
||||
socket.on('close', () => {
|
||||
console.log('VNC socket closed');
|
||||
if (ws.readyState === ws.OPEN) ws.close();
|
||||
});
|
||||
});
|
||||
|
||||
server.on('error', error => {
|
||||
console.error('Server error:', error);
|
||||
});
|
||||
|
||||
server.listen(PORT, '0.0.0.0', () => {
|
||||
console.log(`https://0.0.0.0:${PORT}`);
|
||||
})
|
||||
Reference in New Issue
Block a user