init
This commit is contained in:
@@ -0,0 +1,87 @@
|
||||
<body>
|
||||
<audio controls autoplay>
|
||||
<source src="http://212.47.251.184:5902" type="audio/ogg" />
|
||||
</audio>
|
||||
</body>
|
||||
<script>
|
||||
/*
|
||||
const videoRec = document.createElement('audio');
|
||||
videoRec.autoplay = true;
|
||||
videoRec.controls = true;
|
||||
videoRec.id = 'audio';
|
||||
videoRec.type = 'audio/ogg; codec=opus';
|
||||
videoRec.src = 'http://212.47.251.184:5902';
|
||||
document.body.appendChild(videoRec);
|
||||
*/
|
||||
|
||||
/*
|
||||
const ms = new MediaSource();
|
||||
let queue = [];
|
||||
let buffSize = 0;
|
||||
let first = true;
|
||||
|
||||
ms.onsourceopen = () => {
|
||||
console.log('onsourceopen');
|
||||
ms.addSourceBuffer('audio/mp4; codecs=opus');
|
||||
const sourceBuffer = ms.sourceBuffers[0];
|
||||
const updateNext = () => {
|
||||
if (queue.length) {
|
||||
console.log('premerge', queue.length, buffSize);
|
||||
const merged = mergeBuffers();
|
||||
console.log('merged', merged.map(num => num.toString(16).padStart(2, '0')));
|
||||
// debugger;
|
||||
try {
|
||||
sourceBuffer.appendBuffer(merged);
|
||||
} catch (e) {
|
||||
console.log(document.getElementById('audio').error);
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
sourceBuffer.addEventListener('updateend', updateNext);
|
||||
const socket = new WebSocket('ws://212.47.251.184:5001');
|
||||
socket.binaryType = 'arraybuffer';
|
||||
socket.onmessage = (event) => {
|
||||
const buffer = new Uint8Array(event.data);
|
||||
queue.push(buffer);
|
||||
buffSize += buffer.length;
|
||||
if (!sourceBuffer.updating) {
|
||||
updateNext();
|
||||
}
|
||||
}
|
||||
};
|
||||
ms.onsourceclose = () => {
|
||||
console.log('onsourceclose');
|
||||
}
|
||||
videoRec.src = window.URL.createObjectURL(ms);
|
||||
|
||||
function mergeBuffers() {
|
||||
var merged;
|
||||
|
||||
if (queue.length == 1) {
|
||||
merged = queue[0];
|
||||
} else {
|
||||
merged = new Uint8Array(buffSize);
|
||||
|
||||
var length = queue.length;
|
||||
var offset = 0;
|
||||
|
||||
for (var i = 0; i < length; i++) {
|
||||
var curr = queue[i];
|
||||
if (curr.length <= 0) {
|
||||
continue;
|
||||
}
|
||||
merged.set(curr, offset);
|
||||
offset += curr.length;
|
||||
}
|
||||
}
|
||||
|
||||
queue = [];
|
||||
buffSize = 0;
|
||||
return merged;
|
||||
}
|
||||
*/
|
||||
//options
|
||||
// mp4mux with mediasource and websockify
|
||||
// ogg with TCP->HTTP forwarder and audio src
|
||||
</script>
|
||||
@@ -0,0 +1,11 @@
|
||||
#!/bin/bash
|
||||
|
||||
sudo docker exec -it --user postgres postgres sh -c "pg_dump postgres" > postgres.bak
|
||||
|
||||
sudo docker exec -it redis sh -c "cat dump.rdb" > dump.rdb
|
||||
|
||||
# on local: scp root@ip:dump.rdb dump.rdb
|
||||
|
||||
sudo docker exec -it --user postgres postgres sh -c "psql postgres" < postgres.bak
|
||||
|
||||
sudo docker exec -it redis sh -c "cat > dump.rdb" < dump.rdb
|
||||
@@ -0,0 +1,7 @@
|
||||
FROM siomiz/chrome
|
||||
|
||||
RUN apt-get update
|
||||
|
||||
RUN apt-get install -y libasound2 alsa-utils alsa-oss
|
||||
|
||||
RUN apt-get install -y pulseaudio
|
||||
@@ -0,0 +1,35 @@
|
||||
FROM ubuntu:xenial
|
||||
|
||||
RUN apt-get update; apt-get clean
|
||||
|
||||
# Add a user for running applications.
|
||||
RUN useradd apps
|
||||
RUN mkdir -p /home/apps && chown apps:apps /home/apps
|
||||
|
||||
# Install x11vnc.
|
||||
RUN apt-get install -y x11vnc
|
||||
|
||||
# Install xvfb.
|
||||
RUN apt-get install -y xvfb
|
||||
|
||||
# Install fluxbox.
|
||||
RUN apt-get install -y fluxbox
|
||||
|
||||
# Install wget.
|
||||
RUN apt-get install -y wget
|
||||
|
||||
# Install wmctrl.
|
||||
RUN apt-get install -y wmctrl
|
||||
|
||||
RUN apt-get install -y gnupg2
|
||||
|
||||
# Set the Chrome repo.
|
||||
RUN wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add - \
|
||||
&& echo "deb http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google.list
|
||||
|
||||
# Install Chrome.
|
||||
RUN apt-get update && apt-get -y install google-chrome-stable
|
||||
|
||||
COPY bootstrap.sh /
|
||||
|
||||
CMD '/bootstrap.sh'
|
||||
@@ -0,0 +1,88 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Based on: http://www.richud.com/wiki/Ubuntu_Fluxbox_GUI_with_x11vnc_and_Xvfb
|
||||
|
||||
readonly G_LOG_I='[INFO]'
|
||||
readonly G_LOG_W='[WARN]'
|
||||
readonly G_LOG_E='[ERROR]'
|
||||
|
||||
main() {
|
||||
launch_xvfb
|
||||
launch_window_manager
|
||||
run_vnc_server
|
||||
}
|
||||
|
||||
launch_xvfb() {
|
||||
# Set defaults if the user did not specify envs.
|
||||
export DISPLAY=${XVFB_DISPLAY:-:1}
|
||||
local screen=${XVFB_SCREEN:-0}
|
||||
local resolution=${XVFB_RESOLUTION:-1280x1024x24}
|
||||
local timeout=${XVFB_TIMEOUT:-5}
|
||||
|
||||
# Start and wait for either Xvfb to be fully up or we hit the timeout.
|
||||
Xvfb ${DISPLAY} -screen ${screen} ${resolution} &
|
||||
local loopCount=0
|
||||
until xdpyinfo -display ${DISPLAY} > /dev/null 2>&1
|
||||
do
|
||||
loopCount=$((loopCount+1))
|
||||
sleep 1
|
||||
if [ ${loopCount} -gt ${timeout} ]
|
||||
then
|
||||
echo "${G_LOG_E} xvfb failed to start."
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
launch_window_manager() {
|
||||
local timeout=${XVFB_TIMEOUT:-5}
|
||||
|
||||
# Start and wait for either fluxbox to be fully up or we hit the timeout.
|
||||
fluxbox &
|
||||
local loopCount=0
|
||||
until wmctrl -m > /dev/null 2>&1
|
||||
do
|
||||
loopCount=$((loopCount+1))
|
||||
sleep 1
|
||||
if [ ${loopCount} -gt ${timeout} ]
|
||||
then
|
||||
echo "${G_LOG_E} fluxbox failed to start."
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
run_vnc_server() {
|
||||
local passwordArgument='-nopw'
|
||||
|
||||
if [ -n "${VNC_SERVER_PASSWORD}" ]
|
||||
then
|
||||
local passwordFilePath="${HOME}/x11vnc.pass"
|
||||
if ! x11vnc -storepasswd "${VNC_SERVER_PASSWORD}" "${passwordFilePath}"
|
||||
then
|
||||
echo "${G_LOG_E} Failed to store x11vnc password."
|
||||
exit 1
|
||||
fi
|
||||
passwordArgument=-"-rfbauth ${passwordFilePath}"
|
||||
echo "${G_LOG_I} The VNC server will ask for a password."
|
||||
else
|
||||
echo "${G_LOG_W} The VNC server will NOT ask for a password."
|
||||
fi
|
||||
|
||||
x11vnc -display ${DISPLAY} -forever ${passwordArgument} &
|
||||
wait $!
|
||||
}
|
||||
|
||||
control_c() {
|
||||
echo ""
|
||||
exit
|
||||
}
|
||||
|
||||
#trap control_c SIGINT SIGTERM SIGHUP
|
||||
|
||||
main
|
||||
|
||||
sleep 1
|
||||
google-chrome --start-maximized --no-first-run --display ${DISPLAY}
|
||||
|
||||
exit
|
||||
@@ -0,0 +1,16 @@
|
||||
import http from "node:http";
|
||||
import net from "node:net";
|
||||
|
||||
const port = 5902;
|
||||
|
||||
const client = new net.Socket();
|
||||
const server = http.createServer((req, res) => {
|
||||
console.log(req.url);
|
||||
res.setHeader("Content-Type", "audio/ogg");
|
||||
client.connect(5901, "localhost");
|
||||
client.pipe(res);
|
||||
});
|
||||
|
||||
server.listen(port, () => {
|
||||
console.log(`server is listening on ${port}`);
|
||||
});
|
||||
@@ -0,0 +1,29 @@
|
||||
# Enable kernel modules on the host
|
||||
modprobe snd-aloop
|
||||
|
||||
# Install gstreamer
|
||||
apt-get install -y libgstreamer1.0-0 gstreamer1.0-plugins-base gstreamer1.0-plugins-good gstreamer1.0-plugins-bad gstreamer1.0-plugins-ugly gstreamer1.0-libav gstreamer1.0-doc gstreamer1.0-tools gstreamer1.0-x gstreamer1.0-alsa gstreamer1.0-gl gstreamer1.0-gtk3 gstreamer1.0-qt5 gstreamer1.0-pulseaudio
|
||||
|
||||
# Inside container: ALSA to view sound devices with aplay -l
|
||||
apt-get install -y libasound2 alsa-utils alsa-oss
|
||||
apt-get install -y pulseaudio
|
||||
|
||||
# Rebuild our container
|
||||
docker build . -t browseparty/chrome
|
||||
|
||||
# Run our services
|
||||
# Start chrome + VNC, sharing audio device
|
||||
docker run -d -p 5900:5900 --rm --name chrome --device /dev/snd -e VNC_PASSWORD="password" -e VNC_SCREEN_SIZE="1280x720" -e CHROME_OPTS_OVERRIDE=" --user-data-dir --no-sandbox --no-first-run --window-position=0,0 --force-device-scale-factor=1 --disable-dev-shm-usage" browseparty/chrome
|
||||
|
||||
# Proxy the VNC from TCP to WebSocket
|
||||
docker run -d -it --rm --net=host --name websockify efrecon/websockify 5000 localhost:5900
|
||||
|
||||
# Audio test
|
||||
speaker-test -D hw:0,0,0 --channels 2 --rate 48000
|
||||
|
||||
# Stream the audio to a TCP sink
|
||||
gst-launch-1.0 -v alsasrc device=hw:0,1,0 ! audio/x-raw, channels=2, rate=48000 ! opusenc complexity=0 ! oggmux ! tcpserversink port=5901 host=0.0.0.0
|
||||
gst-launch-1.0 -v alsasrc device=hw:0,1,0 ! audio/x-raw, channels=2, rate=48000 ! audioconvert ! lamemp3enc ! tcpserversink port=5901 host=0.0.0.0
|
||||
|
||||
# Proxy the audio from TCP to WebSocket
|
||||
docker run -d -it --rm --net=host --name websockify-audio efrecon/websockify 5001 localhost:5901
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"CORSRules": [
|
||||
{
|
||||
"AllowedOrigins": ["*"],
|
||||
"AllowedHeaders": ["*"],
|
||||
"AllowedMethods": ["GET", "HEAD", "POST", "PUT", "DELETE"],
|
||||
"MaxAgeSeconds": 3000,
|
||||
"ExposeHeaders": ["Etag"]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
<CORSConfiguration>
|
||||
<CORSRule>
|
||||
<AllowedOrigin>*</AllowedOrigin>
|
||||
<AllowedMethod>GET</AllowedMethod>
|
||||
<AllowedHeader>*</AllowedHeader>
|
||||
<MaxAgeSeconds>3000</MaxAgeSeconds>
|
||||
<ExposeHeader>ETag</ExposeHeader>
|
||||
</CORSRule>
|
||||
</CORSConfiguration>
|
||||
@@ -0,0 +1,15 @@
|
||||
#!/bin/bash
|
||||
|
||||
# install docker
|
||||
curl -fsSL https://get.docker.com | sh
|
||||
|
||||
# coturn
|
||||
docker run -d --network=host --name=coturn coturn/coturn \
|
||||
-n --log-file=stdout \
|
||||
--lt-cred-mech --fingerprint \
|
||||
--no-multicast-peers --no-cli \
|
||||
--no-tlsv1 --no-tlsv1_1 \
|
||||
--realm=watchparty \
|
||||
# doesn't actually work, manually docker exec bash and run the add
|
||||
docker exec -it --user nobody coturn "bash -c 'turnadmin --add -u username -p password -r watchparty'"
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
#!/bin/bash
|
||||
|
||||
apt-get update
|
||||
apt-get install -y curl
|
||||
|
||||
# install docker
|
||||
curl -fsSL https://get.docker.com | sh
|
||||
# pull vbrowser image
|
||||
docker pull howardc93/vbrowser
|
||||
# install certbot
|
||||
DEBIAN_FRONTEND=noninteractive apt-get install -y certbot
|
||||
certbot certonly --standalone -n --email howardzchung@gmail.com --agree-tos -d docker1.watchparty.me
|
||||
chmod -R 755 /etc/letsencrypt/live/
|
||||
chmod -R 755 /etc/letsencrypt/archive/
|
||||
@@ -0,0 +1,36 @@
|
||||
#!/bin/bash
|
||||
|
||||
apt update
|
||||
|
||||
DEBIAN_FRONTEND=noninteractive apt-get install -y certbot python3-certbot-dns-cloudflare
|
||||
certbot certonly --standalone -n --email howardzchung@gmail.com --agree-tos -d $(hostname).watchparty.me
|
||||
#certbot certonly --dns-cloudflare --dns-cloudflare-credentials ~/cloudflare.ini -d *.watchparty.me --preferred-challenges dns-01
|
||||
|
||||
apt install -y nginx
|
||||
apt install -y bind9
|
||||
|
||||
echo 'events {}
|
||||
http {
|
||||
server {
|
||||
listen 443 ssl;
|
||||
server_name HOSTNAME_PLACEHOLDER.watchparty.me;
|
||||
ssl_certificate /etc/letsencrypt/live/HOSTNAME_PLACEHOLDER.watchparty.me/fullchain.pem;
|
||||
ssl_certificate_key /etc/letsencrypt/live/HOSTNAME_PLACEHOLDER.watchparty.me/privkey.pem;
|
||||
|
||||
location / {
|
||||
resolver 127.0.0.1;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header Host $host;
|
||||
proxy_pass http://$arg_ip;
|
||||
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
}
|
||||
}
|
||||
}' | sed "s/HOSTNAME_PLACEHOLDER/$(hostname)/g" > /etc/nginx/nginx.conf
|
||||
/etc/init.d/nginx reload
|
||||
|
||||
# Update /etc/letsencrypt/cli.ini with renew hook
|
||||
echo '
|
||||
deploy-hook = systemctl reload nginx' >> /etc/letsencrypt/cli.ini
|
||||
@@ -0,0 +1,8 @@
|
||||
import { Redis } from "ioredis";
|
||||
|
||||
const redis = new Redis("localhost:6379");
|
||||
const redis2 = new Redis("localhost:6379");
|
||||
redis.blpop("ioRedisTest", 0);
|
||||
redis.disconnect();
|
||||
await redis2.lpush("ioRedisTest", "val");
|
||||
console.log(await redis2.lrange("ioRedisTest", 0, -1));
|
||||
@@ -0,0 +1,2 @@
|
||||
#!/bin/bash
|
||||
docker run --name watchparty-redis --rm --net=host -d redis
|
||||
@@ -0,0 +1,21 @@
|
||||
import fs from "node:fs";
|
||||
import xml2js from "xml2js";
|
||||
|
||||
const xml = ``;
|
||||
|
||||
const subPath = "/var/www/html/subtitles/";
|
||||
const subs = fs.readdirSync(subPath);
|
||||
let vids = fs.readdirSync("/var/www/html/");
|
||||
const data = await xml2js.parseStringPromise(xml);
|
||||
// vids = [...data.ListBucketResult.Contents.filter(file => file.Key[0].startsWith('')).map(file => file.Key[0])];
|
||||
console.log(subs);
|
||||
console.log(vids);
|
||||
vids.forEach((vid) => {
|
||||
const match = subs.find((sub) =>
|
||||
vid.toLowerCase().startsWith(sub.slice(0, -4).toLowerCase()),
|
||||
);
|
||||
console.log(vid, match);
|
||||
if (vid && match) {
|
||||
fs.copyFileSync(subPath + match, subPath + vid + ".srt");
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,29 @@
|
||||
import requests
|
||||
|
||||
url = "https://discord.com/api/v10/applications/1071394728513380372/commands"
|
||||
|
||||
# This is an example CHAT_INPUT or Slash Command, with a type of 1
|
||||
json = {
|
||||
"name": "watch",
|
||||
"type": 1,
|
||||
"description": "Start a new WatchParty",
|
||||
"options": [
|
||||
{
|
||||
"name": "video",
|
||||
"description": "The URL to start watching",
|
||||
"type": 3
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
# For authorization, you can use either your bot token
|
||||
headers = {
|
||||
"Authorization": "Bot <TOKEN>"
|
||||
}
|
||||
|
||||
# or a client credentials token for your app with the applications.commands.update scope
|
||||
# headers = {
|
||||
# "Authorization": "Bearer <my_credentials_token>"
|
||||
# }
|
||||
|
||||
r = requests.post(url, headers=headers, json=json)
|
||||
@@ -0,0 +1,96 @@
|
||||
<video id="video" autoplay="true"></video>
|
||||
<canvas id="canvas"></canvas>
|
||||
<script>
|
||||
var video = document.getElementById("video");
|
||||
|
||||
if (navigator.mediaDevices.getUserMedia) {
|
||||
var successCallback = function (stream) {
|
||||
video.srcObject = stream;
|
||||
var canvas = document.querySelector("canvas");
|
||||
|
||||
// Optional frames per second argument.
|
||||
var stream2 = canvas.captureStream(25);
|
||||
var recordedChunks = [];
|
||||
|
||||
console.log(stream);
|
||||
var options = { mimeType: "video/webm; codecs=vp9" };
|
||||
mediaRecorder = new MediaRecorder(stream, options);
|
||||
|
||||
mediaRecorder.ondataavailable = handleDataAvailable;
|
||||
mediaRecorder.start(1000);
|
||||
|
||||
async function handleDataAvailable(event) {
|
||||
console.log(event.data, await event.data.arrayBuffer());
|
||||
if (event.data.size > 0) {
|
||||
// recordedChunks.push(event.data);
|
||||
addVideoChunk({ data: event.data });
|
||||
}
|
||||
}
|
||||
};
|
||||
var errorCallback = function (error) {
|
||||
console.log(error);
|
||||
};
|
||||
navigator.mediaDevices
|
||||
.getUserMedia({
|
||||
audio: false,
|
||||
video: { facingMode: { ideal: "environment" } }, // prefer rear-facing camera
|
||||
})
|
||||
.then(successCallback, errorCallback);
|
||||
}
|
||||
|
||||
var canvas = document.getElementById("canvas");
|
||||
var context = canvas.getContext("2d");
|
||||
requestAnimationFrame(renderFrame);
|
||||
|
||||
function renderFrame() {
|
||||
// re-register callback
|
||||
requestAnimationFrame(renderFrame);
|
||||
// set internal canvas size to match HTML element size
|
||||
canvas.width = canvas.scrollWidth;
|
||||
canvas.height = canvas.scrollHeight;
|
||||
if (video.readyState === video.HAVE_ENOUGH_DATA) {
|
||||
// scale and horizontally center the camera image
|
||||
var videoSize = { width: video.videoWidth, height: video.videoHeight };
|
||||
var canvasSize = { width: canvas.width, height: canvas.height };
|
||||
var renderSize = calculateSize(videoSize, canvasSize);
|
||||
var xOffset = (canvasSize.width - renderSize.width) / 2;
|
||||
context.drawImage(video, xOffset, 0, renderSize.width, renderSize.height);
|
||||
}
|
||||
}
|
||||
function calculateSize(srcSize, dstSize) {
|
||||
var srcRatio = srcSize.width / srcSize.height;
|
||||
var dstRatio = dstSize.width / dstSize.height;
|
||||
if (dstRatio > srcRatio) {
|
||||
return {
|
||||
width: dstSize.height * srcRatio,
|
||||
height: dstSize.height,
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
width: dstSize.width,
|
||||
height: dstSize.width / srcRatio,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const videoRec = document.createElement("video");
|
||||
const ms = new MediaSource();
|
||||
|
||||
videoRec.autoplay = true;
|
||||
videoRec.width = 320;
|
||||
videoRec.height = 240;
|
||||
// console.log(MediaSource.isTypeSupported('video/webm; codecs=vp9'));
|
||||
ms.onsourceopen = () => {
|
||||
console.log("onsourceopen");
|
||||
ms.addSourceBuffer("video/webm; codecs=vp9");
|
||||
};
|
||||
ms.onsourceclose = () => {
|
||||
console.log("onsourceclose");
|
||||
};
|
||||
videoRec.src = window.URL.createObjectURL(ms);
|
||||
document.body.appendChild(videoRec);
|
||||
|
||||
async function addVideoChunk(data) {
|
||||
ms.sourceBuffers[0].appendBuffer(await data.data.arrayBuffer());
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,12 @@
|
||||
import { resolveShard } from "../server/utils/resolveShard";
|
||||
import { makeName } from "../server/utils/moniker";
|
||||
|
||||
for (let i = 0; i < 100; i++) {
|
||||
const shard = (i % 2) + 1;
|
||||
const name = makeName(shard);
|
||||
const nameShard = resolveShard(name);
|
||||
console.log(name, shard, nameShard);
|
||||
if (shard !== nameShard) {
|
||||
throw new Error("shard mismatch");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
import axios from "axios";
|
||||
import fs from "node:fs";
|
||||
import s3 from "s3-client";
|
||||
|
||||
var client = s3.createClient({
|
||||
maxAsyncS3: 20, // this is the default
|
||||
s3RetryCount: 3, // this is the default
|
||||
s3RetryDelay: 1000, // this is the default
|
||||
multipartUploadThreshold: 2097152000,
|
||||
multipartUploadSize: 15728640, // this is the default (15 MB)
|
||||
s3Options: {
|
||||
accessKeyId: process.env.SCW_ACCESS_KEY,
|
||||
secretAccessKey: process.env.SCW_SECRET_KEY,
|
||||
endpoint: "s3.fr-par.scw.cloud",
|
||||
// endpoint: 's3.yourdomain.com',
|
||||
// sslEnabled: false
|
||||
// any other options are passed to new AWS.S3()
|
||||
// See: http://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/Config.html#constructor-property
|
||||
},
|
||||
});
|
||||
|
||||
var params = {
|
||||
localDir: "/var/www/html",
|
||||
deleteRemoved: false, // default false, whether to remove s3 objects that have no corresponding local file.
|
||||
s3Params: {
|
||||
Bucket: "hcmedia",
|
||||
Prefix: "",
|
||||
},
|
||||
getS3Params: (localFile, stat, callback) => {
|
||||
// call callback like this:
|
||||
var s3Params = {
|
||||
// if there is no error
|
||||
Key: localFile.replace(/ /g, "."),
|
||||
ACL: "public-read",
|
||||
// other options supported by putObject, except Body and ContentLength.
|
||||
// See: http://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/S3.html#putObject-property
|
||||
};
|
||||
// pass `null` for `s3Params` if you want to skip uploading this file.
|
||||
callback(null, s3Params);
|
||||
},
|
||||
};
|
||||
var uploader = client.uploadDir(params);
|
||||
uploader.on("error", function (err) {
|
||||
console.error("unable to sync:", err.stack);
|
||||
});
|
||||
uploader.on("progress", function () {
|
||||
console.log("progress", uploader.progressAmount, uploader.progressTotal);
|
||||
});
|
||||
uploader.on("end", function () {
|
||||
console.log("done uploading");
|
||||
});
|
||||
@@ -0,0 +1,42 @@
|
||||
#!/bin/bash
|
||||
|
||||
# apt-get update
|
||||
# apt-get install -y curl
|
||||
|
||||
# install docker (if not already installed)
|
||||
# curl -fsSL https://get.docker.com | sh
|
||||
|
||||
# install dnsutils (iptables?)
|
||||
apt-get install -y dnsutils
|
||||
|
||||
# disable unattended-upgrades
|
||||
# apt-get remove -y unattended-upgrades
|
||||
|
||||
# determine if this is a large or not via nproc
|
||||
# This systemd config starts the vbrowser on reboot (or instances created from a snapshot of this init vm)
|
||||
echo '
|
||||
#!/bin/bash
|
||||
|
||||
iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 5000
|
||||
PASSWORD=$(uuidgen)
|
||||
#RESOLUTION=$(if [ "$(nproc)" -le "2" ]; then echo "1280x720@30"; else echo "1920x1080@30"; fi)
|
||||
RESOLUTION="1280x720@30"
|
||||
ADMIN_KEY={VBROWSER_ADMIN_KEY}
|
||||
# docker pull howardc93/vbrowser
|
||||
docker run -d --rm --name=vbrowser --log-opt max-size=1g --net=host --shm-size=1g --cap-add="SYS_ADMIN" -e DISPLAY=":99.0" -e NEKO_SCREEN=$RESOLUTION -e NEKO_PASSWORD=$PASSWORD -e NEKO_PASSWORD_ADMIN=$PASSWORD -e NEKO_ADMIN_KEY=$ADMIN_KEY -e NEKO_BIND=":5000" -e NEKO_EPR=":59000-59100" -e NEKO_H264="1" howardc93/vbrowser
|
||||
' > /etc/systemd/system/vbrowser.sh
|
||||
chmod u+x /etc/systemd/system/vbrowser.sh
|
||||
|
||||
echo '[Unit]
|
||||
Description=Start VBrowser
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
ExecStart=bash /etc/systemd/system/vbrowser.sh
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target' > /etc/systemd/system/vbrowser.service
|
||||
systemctl enable vbrowser.service
|
||||
|
||||
# This ensures the image is pre-pulled and also creates an endpoint for updateSnapshot to check
|
||||
docker run -d --rm --name=test --net=host --shm-size=1g --cap-add="SYS_ADMIN" -e DISPLAY=":99.0" -e NEKO_PASSWORD=neko -e NEKO_PASSWORD_ADMIN=admin -e NEKO_BIND=":5000" -e NEKO_EPR=":59000-59100" howardc93/vbrowser
|
||||
@@ -0,0 +1,98 @@
|
||||
# Allow ssh, http, https
|
||||
ufw allow http
|
||||
ufw allow https
|
||||
ufw allow ssh
|
||||
ufw allow 3000
|
||||
# Enable ufw
|
||||
ufw enable
|
||||
|
||||
# Install nginx/bind9
|
||||
apt install -y nginx
|
||||
apt install -y bind9
|
||||
|
||||
echo 'worker_rlimit_nofile 4096;
|
||||
events {
|
||||
worker_connections 4096;
|
||||
}
|
||||
http {
|
||||
|
||||
upstream rr {
|
||||
server 127.0.0.1:3001;
|
||||
server 127.0.0.1:3002;
|
||||
# server 127.0.0.1:3003;
|
||||
}
|
||||
|
||||
upstream 1 {
|
||||
server 127.0.0.1:3001;
|
||||
}
|
||||
|
||||
upstream 2 {
|
||||
server 127.0.0.1:3002;
|
||||
}
|
||||
|
||||
upstream 3 {
|
||||
server 127.0.0.1:3003;
|
||||
}
|
||||
|
||||
map $arg_shard $pool {
|
||||
default "rr";
|
||||
1 "1";
|
||||
2 "2";
|
||||
# 3 "3";
|
||||
}
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
location / {
|
||||
proxy_pass http://$pool;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header Host $host;
|
||||
|
||||
# enable WebSockets
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
}
|
||||
}
|
||||
}' > /etc/nginx/nginx.conf
|
||||
/etc/init.d/nginx reload
|
||||
|
||||
# Install git
|
||||
apt update
|
||||
apt install -y git
|
||||
|
||||
# Clone application code
|
||||
git clone https://github.com/howardchung/watchparty
|
||||
|
||||
# Install docker
|
||||
curl -sSL https://get.docker.com/ | sh
|
||||
# Start Redis
|
||||
sudo docker run --log-opt max-size=1g -d --name redis --restart=always --net=host redis
|
||||
# Start Postgres
|
||||
sudo docker run --log-opt max-size=1g -d --name postgres --restart=always -e POSTGRES_PASSWORD=password --net=host -v $PWD/sql/:/docker-entrypoint-initdb.d/ postgres
|
||||
|
||||
# Install NodeJS
|
||||
curl -sL https://deb.nodesource.com/setup_14.x | sudo -E bash -
|
||||
sudo apt-get install -y nodejs
|
||||
|
||||
# Set up certbot or Cloudflare HTTPS
|
||||
|
||||
# Build watchparty
|
||||
npm run build
|
||||
|
||||
# Set .env config
|
||||
echo '
|
||||
DATABASE_URL=postgresql://postgres@localhost:5432/postgres?sslmode=disable
|
||||
REDIS_URL=localhost:6379
|
||||
' > .env
|
||||
|
||||
# Install PM2 globally
|
||||
npm install -g pm2
|
||||
|
||||
# PM2 start
|
||||
npm run pm2
|
||||
|
||||
# Run on startup
|
||||
./node_modules/pm2/bin/pm2 startup
|
||||
./node_modules/pm2/bin/pm2 save
|
||||
Reference in New Issue
Block a user