mirror of
https://git.waldn.net/git/knotteye/satyr.git
synced 2025-07-04 02:39:39 +00:00
Compare commits
63 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
a8a05a992e | ||
|
ef52cbe629 | ||
|
67694cffd8 | ||
|
3701b0c8fa | ||
|
362381e7db | ||
|
a3341d8b7d | ||
|
8a33b80593 | ||
|
c5921e4e08 | ||
|
943c71d1e0 | ||
|
1afd855e73 | ||
|
9df4b545ec | ||
|
364994decc | ||
|
cccdc8838d | ||
|
69d81ec836 | ||
|
7156accbee | ||
|
814d826ec9 | ||
|
a882285bac | ||
|
57410dc969 | ||
|
5c22c1a738 | ||
|
a1a101c0f1 | ||
|
7f40690820 | ||
|
4c1298cc5c | ||
|
1abb35f9ac | ||
|
2ef1c80813 | ||
|
8cb78a7dd6 | ||
|
f703d5af7f | ||
|
2a121d27ee | ||
|
cc8c4915f9 | ||
|
d0e3507cc0 | ||
|
33accfb8b7 | ||
|
3e073e7f66 | ||
|
47e036cde6 | ||
|
a75a625cd3 | ||
|
7b5a498241 | ||
|
2a5e8d6ec2 | ||
|
12e868456a | ||
|
281d9c7922 | ||
|
fe3ec1bd75 | ||
|
07cba66096 | ||
|
2de486da46 | ||
|
2c073a7cdb | ||
|
8a71680971 | ||
|
0900496d70 | ||
|
34d6fa91df | ||
|
97b6f50b7c | ||
|
94f240d4d6 | ||
|
d8b327752e | ||
|
7107cb4c8f | ||
|
90cce68581 | ||
|
1fa6bf7e81 | ||
|
ab9a9b4585 | ||
|
01744df3cd | ||
|
ab082e5f95 | ||
|
de17128cd2 | ||
|
cfa7c5ab13 | ||
|
81afb7493b | ||
|
9a6e5c8798 | ||
|
06dc05eb8b | ||
|
dfd0bc4881 | ||
|
30a62e6378 | ||
|
cc3876ff4a | ||
|
56c4b94a80 | ||
|
a5c1adfffc |
@ -4,7 +4,7 @@ System dependencies: A stable version of node>=10, mysql3 (or a compatible imple
|
||||
|
||||
### Setup Instructions
|
||||
```bash
|
||||
git clone https://gitlab.com/knotteye/satyr.git
|
||||
git clone https://pond.waldn.net/git/knotteye/satyr.git
|
||||
cd satyr
|
||||
npm install
|
||||
npm run setup
|
||||
|
10
docs/CHAT.md
10
docs/CHAT.md
@ -3,7 +3,7 @@ This is not a guide to using the webchat, this a reference point for writing cli
|
||||
|
||||
Satyr's webchat is based on [socket.io](https://socket.io/), you can find clients for [Java](https://github.com/socketio/socket.io-client-java), [C++](https://github.com/socketio/socket.io-client-cpp), [Swift](https://github.com/socketio/socket.io-client-swift), [Dart](https://github.com/rikulo/socket.io-client-dart), and probably more.
|
||||
|
||||
Socket.IO is loosely reminiscent of IRC in that you will receive events from the server and sent events to it. The following is a list of incoming and outgoing events you will need to handle or send. If you would like to see examples, templates/chat.html is implementation used in the webclient by satyr.
|
||||
Socket.IO is loosely reminiscent of IRC in that you will receive events from the server and sent events to it. The following is a list of incoming and outgoing events you will need to handle or send. If you would like to see examples, templates/chat.html is the implementation used in the webclient by satyr.
|
||||
|
||||
# Incoming Events
|
||||
These are events you will recieve from the server that need to be handled in some way.
|
||||
@ -53,12 +53,12 @@ This is a request to set the client's nickname. The data attached to a NICK even
|
||||
password: "the optional password"
|
||||
}
|
||||
```
|
||||
During the initial connect of the client, the server will check for the "Authorization" cookie. If the cookie is a valid, signed JWT, the client will be assigned the nickname of the user that cookie belongs to. If it doesn't exist or is invalid, the client will be assigned a nickname of the form Guest+some integer.
|
||||
During the initial connect of the client, the server will check for the "Authorization" cookie. If the cookie is a valid, signed JWT the client will be assigned the nickname of the user that cookie belongs to. If it doesn't exist or is invalid, the client will be assigned a nickname of the form Guest+some integer.
|
||||
|
||||
The server will send an alert notifying the client of either the nickname change, or some error.
|
||||
|
||||
## MSG
|
||||
This is a chat message to send to room. It should be a JSON object in the following format:
|
||||
This is a chat message to send to a room. It should be a JSON object in the following format:
|
||||
```
|
||||
{
|
||||
room: "the room to send the messag to",
|
||||
@ -117,10 +117,10 @@ A request to unban an IP address. It can only be done by the owner of the room.
|
||||
|
||||
# Final Notes
|
||||
|
||||
Sending more than 10 messages a second will cause the server to kick your client. If kicked this way 3 times, the client will be banned for 20 minutes.
|
||||
Sending more than 10 messages per second will cause the server to kick your client. If kicked this way 3 times, the client will be banned for 20 minutes.
|
||||
|
||||
Kicked or banned users will not be notified of this through an event.
|
||||
|
||||
The server *will* send your own MSG events back to you, you will need to parse them out if you want to append them immediately.
|
||||
The server *will* send your own MSG events back to you, you will need to parse them out if you want to display them immediately.
|
||||
|
||||
Clients who successfully authenticate as a registered user, through either a password or a signed JWT, can ignore nickname collision and have as many connections as they wish.
|
@ -7,11 +7,23 @@ Some values you might want to change are
|
||||
satyr:
|
||||
registration: true
|
||||
# allow new users to register
|
||||
port: 8000
|
||||
# the port to serve http on
|
||||
|
||||
http:
|
||||
hsts: true
|
||||
# enable strict transport security
|
||||
|
||||
rtmp:
|
||||
port: 1935
|
||||
# change the port to serve rtmp on
|
||||
cluster: false
|
||||
# enable clustering for the RTMP server
|
||||
# clustering is an attempt to take better advantage of multi threaded systems in spite of node.js being single-threaded
|
||||
# satyr will spawn one RTMP Worker per CPU core, and round-robin incoming connections between workers
|
||||
# If you turn this on, satyr will no longer be able to reliably serve RTMP streams to clients
|
||||
# Your users will have to use DASH instead
|
||||
|
||||
media:
|
||||
record: true
|
||||
# allow users to record VODs
|
||||
@ -26,17 +38,16 @@ transcode:
|
||||
# satyr will generate one source quality variant, and the remaining
|
||||
# variants will be of incrementally lower quality and bitrate
|
||||
|
||||
# having more than 4-5 variants will start giving diminishing returns on stream quality for cpu load
|
||||
# if you can't afford to generate at least 3 variants, it's recommended to leave adaptive streaming off
|
||||
|
||||
inputflags: ""
|
||||
# additional flags to apply to the input during transcoding
|
||||
outputflags: ""
|
||||
# additional flags to apply to the output during transcoding
|
||||
|
||||
# hardware acceleration is a bit difficult to configure programmatically
|
||||
# this is a good place to do so for your system
|
||||
# https://trac.ffmpeg.org/wiki/HWAccelIntro is a good place to start
|
||||
|
||||
# having more than 4-5 variants will start giving diminishing returns on stream quality for cpu load
|
||||
# if you can't afford to generate at least 3 variants, it's reccomended to leave adaptive streaming off
|
||||
hwaccel:
|
||||
# See HWACCEL.md for information on configuring hardware acceleration.
|
||||
|
||||
crypto:
|
||||
saltRounds: 12
|
||||
@ -44,12 +55,11 @@ crypto:
|
||||
# if you don't understand the implications, don't change this
|
||||
|
||||
chat:
|
||||
# the following settings are for chat mirroring bots
|
||||
# users will still need to choose which channel to mirror
|
||||
# the following settings are for chat bridging bots
|
||||
# users will still need to choose which channel to bridge
|
||||
# for their chat at /profile/chat
|
||||
irc:
|
||||
enabled: true
|
||||
# enable irc mirroring
|
||||
server: chat.freenode.net
|
||||
port: 6697
|
||||
tls: true
|
||||
@ -61,7 +71,6 @@ chat:
|
||||
|
||||
discord:
|
||||
enabled: true
|
||||
# enabled discord integration
|
||||
token: abcdefghijklmnopqrstuvwxyz
|
||||
# the access token for the bot
|
||||
# note that the bot will mirror every channel matching the name the user has chosen
|
||||
@ -74,6 +83,20 @@ chat:
|
||||
# access token for the twitch chat bot
|
||||
# this is not the account password, you will need to generate a token here:
|
||||
# https://twitchapps.com/tmi/
|
||||
|
||||
xmpp:
|
||||
enabled: true
|
||||
server: 'example.com'
|
||||
port: 5222
|
||||
jid: 'exampleBot@example.com'
|
||||
password: 'abcde'
|
||||
# connection settings for the bot
|
||||
nickname: 'SatyrChat'
|
||||
# the nickname the bot will join MUCs with
|
||||
|
||||
# note that for the best experience you should set the default number of history messages to 0 for the MUC
|
||||
# The bot will attempt to request 0 history messages anyway, and will also attempt to ignore any history messages it receives
|
||||
# but both of these things are unreliable
|
||||
```
|
||||
|
||||
### Web Frontend
|
||||
|
53
docs/HWACCEL.md
Normal file
53
docs/HWACCEL.md
Normal file
@ -0,0 +1,53 @@
|
||||
## Configuration Hardware Acceleration
|
||||
Satyr supports the NVENC and VA-API hardware acceleration APIs. If you've configured your system correctly (the hard part) it should be enough to set the type and use the default device setting if you only have one hardware acceleration device.
|
||||
|
||||
### System
|
||||
Configuring the system for any hardware acceleration API involves three main steps: selecting the right drivers, installing the API libraries, and configuring ffmpeg.
|
||||
|
||||
#### NVENC
|
||||
NVENC in ffmpeg can work with either open-source drivers (nouvea) or nvidia's proprietary drivers. The documentation for your distribution should have instructions for installing these.
|
||||
|
||||
The only system library you should need is the CUDA toolkit, general named cudatoolkit, nvidia-cuda-toolkit, or some variation in your system repositories.
|
||||
You can also try installing manually from [here](https://developer.nvidia.com/cuda-downloads).
|
||||
|
||||
Most binary distributions provide a version of ffmpeg with NVENC already enabled. If not you can try compiling ffmpeg from source with the `--enable-nvenc` flag. If you use a source based distribution you should be familiar with enabling optional compile flags.
|
||||
|
||||
You can verify that ffmpeg has been set up correctly by checking the output of `ffmpeg -hide_banner -hwaccels | grep cuvid` and `ffmpeg -hide_banner -encoders | grep nvenc`. If you don't see anything, something is wrong.
|
||||
|
||||
#### VA-API
|
||||
VA-API is an extremely generic API. Although the package names might be different in your distribution, the arch wiki page for hardware acceleration has good information on [driver selection](https://wiki.archlinux.org/index.php/Hardware_video_acceleration#Installation) and [verifying](https://wiki.archlinux.org/index.php/Hardware_video_acceleration#Verifying_VA-API) a VA-API install for a wide range of devices.
|
||||
|
||||
Regardless of driver selection, you will also need libva or the equivalent from your distrubtion, and libva-utils can be helpful as well.
|
||||
|
||||
Most binary distributions provide a version of ffmpeg with VA-API already enabled. If not you can try compiling ffmpeg from source with the `--enable-vaapi` flag. If you use a source based distribution you should be familiar with enabling optional compile flags.
|
||||
|
||||
You can verify that ffmpeg has been set up correctly by checking the output of `ffmpeg -hide_banner -hwaccels | grep vaapi` and `ffmpeg -hide_banner -encoders | grep vaapi`. If you don't see anything, something is wrong.
|
||||
|
||||
### Satyr
|
||||
```
|
||||
# Decoding
|
||||
hwaccel:
|
||||
# Enable hardware acceleration for decoding as well as encoding.
|
||||
# Probably not worth it, hardware decoding won't be any faster compared to software on a vaguely modern CPU
|
||||
# Hardware decoding also may not support the input format, in which case transcoding will fail
|
||||
decode: true
|
||||
|
||||
# Only supported for VA-API
|
||||
# Fall back to software decoding if hardware decoding fails
|
||||
hwaccel:
|
||||
decode: 'fallback'
|
||||
|
||||
|
||||
# NVENC
|
||||
hwaccel:
|
||||
type: 'nvenc'
|
||||
# device is optional for nvenc
|
||||
device: 0
|
||||
# nvenc wants a device number instead of a path, set to null to use the default
|
||||
|
||||
# VA-API
|
||||
hwaccel:
|
||||
type: 'vaapi'
|
||||
# device is mandatory for va-api
|
||||
device: '/dev/dri/renderD128'
|
||||
```
|
@ -4,14 +4,14 @@ A more detailed walkthrough.
|
||||
### System Dependencies
|
||||
Install ffmpeg(>= 4.2.1) and mysql through your distribution's package manager.
|
||||
See [this page](https://nodejs.org/en/download/package-manager/) for instructions on installing node v10.
|
||||
If the version in your distro's package manager is different, you can install 'n' through npm to manage node versions.
|
||||
If the version in your distro's package manager is different, you can install [n](https://www.npmjs.com/package/n) through npm to manage node versions.
|
||||
|
||||
### Installing Satyr
|
||||
Before starting, you should create a system user to run the satyr service.
|
||||
|
||||
Clone the repository and change to the directory
|
||||
```bash
|
||||
git clone https://gitlab.com/knotteye/satyr.git
|
||||
git clone https://pond.waldn.net/git/knotteye/satyr.git
|
||||
cd satyr
|
||||
```
|
||||
Install nodejs dependencies
|
||||
@ -33,8 +33,7 @@ Then start the server.
|
||||
npm run start
|
||||
```
|
||||
|
||||
It is reccomended that you run Satyr behind a TLS terminating reverse proxy like nginx. An example configuration can be found in CONFIGURATION.md
|
||||
An example systemd service is provided at install/satyr.service. It assumes you've installed satyr into /opt/satyr, and created a satyr user with the home directory /var/lib/satyr for the purpose of running the service.
|
||||
It is reccomended that you run Satyr behind a TLS terminating reverse proxy like nginx. An example configuration can be found at install/satyr.nginx. An example systemd service is provided at install/satyr.service. It assumes you've installed satyr into /opt/satyr, and created a satyr user with the home directory /var/lib/satyr for the purpose of running the service.
|
||||
|
||||
## Updating Satyr
|
||||
Updating should be as simple as pulling the latest code and dependencies, then restarting the server.
|
||||
|
29
docs/REST.md
29
docs/REST.md
@ -26,12 +26,13 @@ Configuration of the instance relating to media
|
||||
|
||||
**Parameters**: none
|
||||
|
||||
**Response**: JSON object containing the port and ping_timeout for RTMP, public and private play endpoints, and whether adaptive livestreaming and VOD recording are enabled.
|
||||
**Response**: JSON object containing the port and ping_timeout for RTMP, whether clustering is enabled, public and private play endpoints, and whether adaptive livestreaming and VOD recording are enabled.
|
||||
|
||||
**Example**:
|
||||
```
|
||||
{
|
||||
rtmp: {
|
||||
cluster: false,
|
||||
port: 1935,
|
||||
ping_timeout: 60
|
||||
},
|
||||
@ -68,7 +69,7 @@ sort is the optional way to sort results. current options are "alphabet" and "al
|
||||
|
||||
page is the page number (i.e. skip the first num * page results)
|
||||
|
||||
**Response**: Returns an array of JSON objects containing the username and title of each stream. Returns an empty array if no one is streaming. Returns `{"error":"error code"}` in the event of an error. Will attempt to correct malformed requests to default values.
|
||||
**Response**: Returns an array of JSON objects containing the username and title of each stream. Returns an empty array if no one is streaming. Returns `{"error":"error reason"}` in the event of an error. Will attempt to correct malformed requests to default values.
|
||||
|
||||
The array will be wrapped in a JSON object under the key 'users'.
|
||||
|
||||
@ -77,7 +78,7 @@ The array will be wrapped in a JSON object under the key 'users'.
|
||||
|
||||
## /api/users/all
|
||||
|
||||
Same as above, but returns all users regardless of whether they are streaming and if they're streaming or not.
|
||||
Same as above, but returns all users regardless of whether they are streaming and if they're streaming or not. Also returns a value 'live' indicating whether a user is currently streaming.
|
||||
|
||||
**Example**: `{users: [{username:"foo", title:"bar", live:1}] }`
|
||||
|
||||
@ -130,7 +131,7 @@ Update the current user's information
|
||||
|
||||
Rec is a boolean (whether to record VODs), twitch is a boolean (whether to mirror video streams to twitch) others are strings. Twitch_key is the stream key to use for twitch. Parameters that are not included in the request will not be updated.
|
||||
|
||||
**Response**: Returns `{error: "error code"}` or `{success: ""}`
|
||||
**Response**: Returns `{error: "error reason"}` or `{success: ""}`
|
||||
|
||||
|
||||
|
||||
@ -144,9 +145,9 @@ Update the chatrooms on other platforms to integrate with the user's stream chat
|
||||
|
||||
**Parameters**: discord, xmpp, twitch irc
|
||||
|
||||
Strings corresponding to a channel name to mirror to. XMPP is currently unused. Parameters not included in the request will not be updated.
|
||||
Strings corresponding to a channel name to mirror to. Parameters not included in the request will not be updated.
|
||||
|
||||
**Response**: Returns `{error: "error code"}` or `{success: ""}`
|
||||
**Response**: Returns `{error: "error reason"}` or `{success: ""}`
|
||||
|
||||
|
||||
|
||||
@ -158,9 +159,9 @@ Delete the specified vods of the current user
|
||||
|
||||
**Authentication**: yes
|
||||
|
||||
**Paramters**: A string array of the names of vods to be deleted. (Do not include the file extension);
|
||||
**Paramters**: A string array of the names of vods to be deleted. (Do not include the file extension)
|
||||
|
||||
**Response**: Returns `{error: "error code"}` or `{success: ""}`
|
||||
**Response**: Returns `{error: "error reason"}` or `{success: ""}`
|
||||
|
||||
## /api/user/password
|
||||
|
||||
@ -172,7 +173,7 @@ Change the current user's password
|
||||
|
||||
**Parameters**: The user's current password, the new password, AND a valid JWT cookie.
|
||||
|
||||
**Response**: Returns `{error: "error code"}` or `{success: ""}`
|
||||
**Response**: Returns `{error: "error reason"}` or `{success: ""}`
|
||||
|
||||
|
||||
|
||||
@ -186,7 +187,7 @@ Change the current user's stream key. This will not affect the stream if the use
|
||||
|
||||
**Parameters**: A valid JWT cookie. No other parameters.
|
||||
|
||||
**Response**: Returns `{error: "error code"}` or `{success: "new_stream_key"}`
|
||||
**Response**: Returns `{error: "error reason"}` or `{success: "new_stream_key"}`
|
||||
|
||||
|
||||
|
||||
@ -202,7 +203,7 @@ Get a list of the named users VODs
|
||||
|
||||
**Response**: Returns an array of VODs inside a JSON object with the format `{"vods": [{"name":"yote.mp4"},{"name":"yeet.mp4"}] }`
|
||||
|
||||
**Notes**: VODs are always available at http://domain.com/publicEndpoint/username/filename.mp4
|
||||
**Notes**: VODs are always available at http://domain/publicEndpoint/username/filename
|
||||
|
||||
|
||||
|
||||
@ -216,9 +217,9 @@ Get information about the specified user.
|
||||
|
||||
**Parameters**: user
|
||||
|
||||
**Response**: Returns a JSON object with available information about the user. If the user is authenticated and searching for their own information, will return all available information. Otherwise it will return only the stream title and bio. In the case of searching for a user that does not exist, the returned object will contain only the username searched for.
|
||||
**Response**: Returns a JSON object with available information about the user. If the user is authenticated and searching for their own information, will return all available information. Otherwise it will return only the stream title, bio, and whether the stream is live. In the case of searching for a user that does not exist, the returned object will contain only the username searched for.
|
||||
|
||||
**Example**: `{username: "foo", title: "bar", about: "This is an example bio"}`
|
||||
**Example**: `{username: "foo", title: "bar", about: "This is an example bio", live: 0}`
|
||||
|
||||
|
||||
|
||||
@ -234,4 +235,4 @@ Returns the current stream key for the authenticated user.
|
||||
|
||||
**Response**: returns a JSON object with the stream key
|
||||
|
||||
**Example**: `{"stream_key": "abcdefghijklmno12345"}` or `{"error":"error reason"}`
|
||||
**Example**: `{stream_key: "abcdefghijklmno12345"}` or `{error:"error reason"}`
|
@ -1,47 +1,36 @@
|
||||
## Satyr Usage
|
||||
|
||||
### Administration
|
||||
Satyr needs access to port 1935 for RTMP streams, and will serve HTTP on port 8000. The ports can be changed with follow config lines.
|
||||
Satyr needs access to port 1935 for RTMP streams, and will serve HTTP on port 8000. See CONFIGURATION.md for details on changing this.
|
||||
|
||||
For HTTPS, run a reverse proxy in front of satyr. An example nginx block is shown below.
|
||||
```
|
||||
server {
|
||||
port 80;
|
||||
port [::]80;
|
||||
server_name example.tld;
|
||||
return https://$server_name$request_uri 301;
|
||||
}
|
||||
server {
|
||||
port 443 ssl;
|
||||
port [::]443 ssl;
|
||||
server_name example.tld;
|
||||
For HTTPS, run a reverse proxy in front of satyr. An example nginx config can be found at install/satyr.nginx
|
||||
An example systemd service can also be at install/satyr.service
|
||||
|
||||
ssl_trusted_certificate /etc/letsencrypt/live/example.tld/chain.pem;
|
||||
ssl_certificate /etc/letsencrypt/live/example.tld/fullchain.pem;
|
||||
ssl_certificate_key /etc/letsencrypt/live/example.tld/privkey.pem;
|
||||
#### CLI
|
||||
Satyr's CLI tool can be run with `npm run cli` or `node_modules/.bin/ts-node src/cli.ts`
|
||||
|
||||
It's not very complex. The following commands are available:
|
||||
* `npm run cli -- --adduser sally --password "hunter12"` to create user sally with the password hunter12
|
||||
* `npm run cli -- --rmuser sally` to remove user sally
|
||||
* `npm run cli -- --invite` to generate an invite code used for creating account even when registration is closed
|
||||
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:8000/;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Users
|
||||
|
||||
#### Accounts
|
||||
If registration is open, users can register at example.tld/registration, and set some options such as whether to record VODs and a stream title on /profile
|
||||
Stream keys can be changed at example.tld/changesk, and passwords at /changepwd
|
||||
If registration is open, users can register at your.site.com/registration, and set some options such as whether to record VODs and a stream title on /profile.
|
||||
|
||||
#### Chat
|
||||
Chat is based on Socket.IO, and can be accessed through the webclient at /chat.
|
||||
Chatting and changing a nickname do not require authentication, but the usernames of streamers are reserved.
|
||||
|
||||
The following commands are available:
|
||||
`/nick sally (password)` Password is only required if sally is a registered user.
|
||||
`/join sally` Join the chatroom for sally's stream and leave the previous room.
|
||||
`/kick bob` Available only in your own room if you are a streamer. Forcefully disconnect the user.
|
||||
`/ban bob (time)` Ban a user from your room. Bans are based on IP address. The optional time is in minutes. The default is 30.
|
||||
`/banlist` List the IPs currently banned from your room.
|
||||
`/unban (ip)` self explanatory
|
||||
* `/nick sally (password)` Password is only required if sally is a registered user.
|
||||
* `/join sally` Join the chatroom for sally's stream and leave the previous room.
|
||||
* `/kick bob` Available only in your own room if you are a streamer. Forcefully disconnect the user.
|
||||
* `/ban bob (time)` Ban a user from your room. Bans are based on IP address. The optional time is in minutes. The default is 30.
|
||||
* `/banlist` List the IPs currently banned from your room.
|
||||
* `/unban (ip)` self explanatory
|
||||
|
||||
You can set up mirroring to and from webchat rooms and IRC channels, twitch streams, and discord server channels.
|
||||
More information is in CONFIGURATION.md
|
||||
|
@ -28,10 +28,15 @@ database:
|
||||
transcode:
|
||||
#may result in higher latency if your cpu can't keep up
|
||||
adaptive: false
|
||||
#more than 3 might cause problems when using hwacceleration
|
||||
variants: 3
|
||||
#unused right now, will always transcode to dash
|
||||
format: dash
|
||||
|
||||
hwaccel:
|
||||
# see docs/HWACCEL.md for instructions on configuring hardware acceleration
|
||||
type: null
|
||||
|
||||
chat:
|
||||
|
||||
irc:
|
||||
@ -51,10 +56,11 @@ chat:
|
||||
|
||||
xmpp:
|
||||
enabled: false
|
||||
server:
|
||||
server:
|
||||
port: 5222
|
||||
nickname: 'SatyrChat'
|
||||
username: 'SatyrChat'
|
||||
jid:
|
||||
password:
|
||||
nickname:
|
||||
|
||||
twitch:
|
||||
enabled: false
|
||||
@ -68,4 +74,4 @@ twitch_mirror:
|
||||
enabled: false
|
||||
# https://stream.twitch.tv/ingests/
|
||||
# do not include {stream_key}
|
||||
ingest: 'rtmp://live-ord02.twitch.tv/app/
|
||||
ingest: 'rtmp://live-ord02.twitch.tv/app/'
|
29
install/satyr.nginx
Normal file
29
install/satyr.nginx
Normal file
@ -0,0 +1,29 @@
|
||||
server {
|
||||
port 80;
|
||||
port [::]80;
|
||||
server_name example.tld;
|
||||
return https://$server_name$request_uri 301;
|
||||
}
|
||||
server {
|
||||
port 443 ssl;
|
||||
port [::]443 ssl;
|
||||
server_name example.tld;
|
||||
|
||||
ssl_trusted_certificate /etc/letsencrypt/live/example.tld/chain.pem;
|
||||
ssl_certificate /etc/letsencrypt/live/example.tld/fullchain.pem;
|
||||
ssl_certificate_key /etc/letsencrypt/live/example.tld/privkey.pem;
|
||||
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:8000/;
|
||||
}
|
||||
|
||||
location ~* \.(mpd|m4s|mp4)$ {
|
||||
add_header 'Access-Control-Allow-Origin' '*';
|
||||
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
|
||||
add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
|
||||
add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range';
|
||||
# nginx can serve static files faster than node
|
||||
# this should improve performance
|
||||
root /opt/satyr/site/;
|
||||
}
|
||||
}
|
523
package-lock.json
generated
523
package-lock.json
generated
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "satyr",
|
||||
"version": "0.10.0",
|
||||
"version": "0.10.2",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
@ -22,6 +22,30 @@
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.67.tgz",
|
||||
"integrity": "sha512-R48tgL2izApf+9rYNH+3RBMbRpPeW3N8f0I9HMhggeq4UXwBDqumJ14SDs4ctTMhG11pIOduZ4z3QWGOiMc9Vg=="
|
||||
},
|
||||
"@xmpp/jid": {
|
||||
"version": "0.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@xmpp/jid/-/jid-0.0.2.tgz",
|
||||
"integrity": "sha1-DVKMqdWNr8gzZlVk/+YvMyoxZ/I="
|
||||
},
|
||||
"@xmpp/streamparser": {
|
||||
"version": "0.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@xmpp/streamparser/-/streamparser-0.0.6.tgz",
|
||||
"integrity": "sha1-EYAz6p23yGoctGED8mnr/3n28eo=",
|
||||
"requires": {
|
||||
"@xmpp/xml": "^0.1.3",
|
||||
"inherits": "^2.0.3",
|
||||
"ltx": "^2.5.0"
|
||||
}
|
||||
},
|
||||
"@xmpp/xml": {
|
||||
"version": "0.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@xmpp/xml/-/xml-0.1.3.tgz",
|
||||
"integrity": "sha1-HxQ5nlPkGWiFWGmPbGLnHjmoam4=",
|
||||
"requires": {
|
||||
"inherits": "^2.0.3",
|
||||
"ltx": "^2.6.2"
|
||||
}
|
||||
},
|
||||
"a-sync-waterfall": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/a-sync-waterfall/-/a-sync-waterfall-1.0.1.tgz",
|
||||
@ -46,6 +70,17 @@
|
||||
"resolved": "https://registry.npmjs.org/after/-/after-0.8.2.tgz",
|
||||
"integrity": "sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8="
|
||||
},
|
||||
"ajv": {
|
||||
"version": "6.12.6",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
|
||||
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
|
||||
"requires": {
|
||||
"fast-deep-equal": "^3.1.1",
|
||||
"fast-json-stable-stringify": "^2.0.0",
|
||||
"json-schema-traverse": "^0.4.1",
|
||||
"uri-js": "^4.2.2"
|
||||
}
|
||||
},
|
||||
"ansi-regex": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
|
||||
@ -108,6 +143,14 @@
|
||||
"resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz",
|
||||
"integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY="
|
||||
},
|
||||
"asn1": {
|
||||
"version": "0.2.4",
|
||||
"resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz",
|
||||
"integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==",
|
||||
"requires": {
|
||||
"safer-buffer": "~2.1.0"
|
||||
}
|
||||
},
|
||||
"asn1.js": {
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.2.0.tgz",
|
||||
@ -118,16 +161,41 @@
|
||||
"minimalistic-assert": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"assert-plus": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
|
||||
"integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU="
|
||||
},
|
||||
"async-limiter": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz",
|
||||
"integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ=="
|
||||
},
|
||||
"asynckit": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
|
||||
"integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k="
|
||||
},
|
||||
"aws-sign2": {
|
||||
"version": "0.7.0",
|
||||
"resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz",
|
||||
"integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg="
|
||||
},
|
||||
"aws4": {
|
||||
"version": "1.10.1",
|
||||
"resolved": "https://registry.npmjs.org/aws4/-/aws4-1.10.1.tgz",
|
||||
"integrity": "sha512-zg7Hz2k5lI8kb7U32998pRRFin7zJlkfezGJjUc2heaD4Pw2wObakCDVzkKztTm/Ln7eiVvYsjqak0Ed4LkMDA=="
|
||||
},
|
||||
"backo2": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz",
|
||||
"integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc="
|
||||
},
|
||||
"backoff": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/backoff/-/backoff-2.3.0.tgz",
|
||||
"integrity": "sha1-7nx+OAk/kuRyhZ22NedlJFT8Ieo="
|
||||
},
|
||||
"balanced-match": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
|
||||
@ -157,6 +225,21 @@
|
||||
"node-pre-gyp": "0.15.0"
|
||||
}
|
||||
},
|
||||
"bcrypt-pbkdf": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
|
||||
"integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=",
|
||||
"requires": {
|
||||
"tweetnacl": "^0.14.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"tweetnacl": {
|
||||
"version": "0.14.5",
|
||||
"resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
|
||||
"integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q="
|
||||
}
|
||||
}
|
||||
},
|
||||
"better-assert": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/better-assert/-/better-assert-1.0.2.tgz",
|
||||
@ -236,6 +319,11 @@
|
||||
"fill-range": "^7.0.1"
|
||||
}
|
||||
},
|
||||
"browser-request": {
|
||||
"version": "0.3.3",
|
||||
"resolved": "https://registry.npmjs.org/browser-request/-/browser-request-0.3.3.tgz",
|
||||
"integrity": "sha1-ns5bWsqJopkyJC4Yv5M975h2zBc="
|
||||
},
|
||||
"buffer-from": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz",
|
||||
@ -251,6 +339,11 @@
|
||||
"resolved": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz",
|
||||
"integrity": "sha1-KAOY5dZkvXQDi28JBRU+borxvCA="
|
||||
},
|
||||
"caseless": {
|
||||
"version": "0.12.0",
|
||||
"resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
|
||||
"integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw="
|
||||
},
|
||||
"chalk": {
|
||||
"version": "2.4.2",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
|
||||
@ -300,6 +393,14 @@
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
|
||||
"integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="
|
||||
},
|
||||
"combined-stream": {
|
||||
"version": "1.0.8",
|
||||
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
|
||||
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
|
||||
"requires": {
|
||||
"delayed-stream": "~1.0.0"
|
||||
}
|
||||
},
|
||||
"commander": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-3.0.2.tgz",
|
||||
@ -390,6 +491,14 @@
|
||||
"ts-toolbelt": "^6.9.0"
|
||||
}
|
||||
},
|
||||
"dashdash": {
|
||||
"version": "1.14.1",
|
||||
"resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
|
||||
"integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=",
|
||||
"requires": {
|
||||
"assert-plus": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"dateformat": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/dateformat/-/dateformat-3.0.3.tgz",
|
||||
@ -431,6 +540,11 @@
|
||||
"resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz",
|
||||
"integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA=="
|
||||
},
|
||||
"delayed-stream": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
|
||||
"integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk="
|
||||
},
|
||||
"delegates": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
|
||||
@ -506,11 +620,25 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"ecc-jsbn": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz",
|
||||
"integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=",
|
||||
"requires": {
|
||||
"jsbn": "~0.1.0",
|
||||
"safer-buffer": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"ee-first": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
|
||||
"integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
|
||||
},
|
||||
"eme-encryption-scheme-polyfill": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/eme-encryption-scheme-polyfill/-/eme-encryption-scheme-polyfill-2.0.1.tgz",
|
||||
"integrity": "sha512-Wz+Ro1c0/2Wsx2RLFvTOO0m4LvYn+7cSnq3XOvRvLLBq8jbvUACH/zpU9s0/5+mQa5oaelkU69x+q0z/iWYrFA=="
|
||||
},
|
||||
"encodeurl": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
|
||||
@ -685,6 +813,26 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"extend": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
|
||||
"integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="
|
||||
},
|
||||
"extsprintf": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz",
|
||||
"integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU="
|
||||
},
|
||||
"fast-deep-equal": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
|
||||
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
|
||||
},
|
||||
"fast-json-stable-stringify": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
|
||||
"integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="
|
||||
},
|
||||
"fill-range": {
|
||||
"version": "7.0.1",
|
||||
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
|
||||
@ -728,6 +876,21 @@
|
||||
"resolved": "https://registry.npmjs.org/flags/-/flags-0.1.3.tgz",
|
||||
"integrity": "sha1-lh0vyM3zZp1jBB4w5bJK2tNvV1g="
|
||||
},
|
||||
"forever-agent": {
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
|
||||
"integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE="
|
||||
},
|
||||
"form-data": {
|
||||
"version": "2.3.3",
|
||||
"resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz",
|
||||
"integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==",
|
||||
"requires": {
|
||||
"asynckit": "^0.4.0",
|
||||
"combined-stream": "^1.0.6",
|
||||
"mime-types": "^2.1.12"
|
||||
}
|
||||
},
|
||||
"forwarded": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz",
|
||||
@ -772,6 +935,14 @@
|
||||
"wide-align": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"getpass": {
|
||||
"version": "0.1.7",
|
||||
"resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
|
||||
"integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=",
|
||||
"requires": {
|
||||
"assert-plus": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"glob": {
|
||||
"version": "7.1.6",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
|
||||
@ -794,6 +965,20 @@
|
||||
"is-glob": "^4.0.1"
|
||||
}
|
||||
},
|
||||
"har-schema": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz",
|
||||
"integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI="
|
||||
},
|
||||
"har-validator": {
|
||||
"version": "5.1.5",
|
||||
"resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz",
|
||||
"integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==",
|
||||
"requires": {
|
||||
"ajv": "^6.12.3",
|
||||
"har-schema": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"has-binary2": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/has-binary2/-/has-binary2-1.0.3.tgz",
|
||||
@ -824,6 +1009,33 @@
|
||||
"resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
|
||||
"integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk="
|
||||
},
|
||||
"hash-base": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz",
|
||||
"integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==",
|
||||
"requires": {
|
||||
"inherits": "^2.0.4",
|
||||
"readable-stream": "^3.6.0",
|
||||
"safe-buffer": "^5.2.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"readable-stream": {
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
|
||||
"integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
|
||||
"requires": {
|
||||
"inherits": "^2.0.3",
|
||||
"string_decoder": "^1.1.1",
|
||||
"util-deprecate": "^1.0.1"
|
||||
}
|
||||
},
|
||||
"safe-buffer": {
|
||||
"version": "5.2.1",
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
|
||||
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"http-errors": {
|
||||
"version": "1.7.2",
|
||||
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz",
|
||||
@ -843,6 +1055,16 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"http-signature": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
|
||||
"integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=",
|
||||
"requires": {
|
||||
"assert-plus": "^1.0.0",
|
||||
"jsprim": "^1.2.2",
|
||||
"sshpk": "^1.7.0"
|
||||
}
|
||||
},
|
||||
"iconv": {
|
||||
"version": "2.2.3",
|
||||
"resolved": "https://registry.npmjs.org/iconv/-/iconv-2.2.3.tgz",
|
||||
@ -888,9 +1110,9 @@
|
||||
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
|
||||
},
|
||||
"ini": {
|
||||
"version": "1.3.5",
|
||||
"resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz",
|
||||
"integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw=="
|
||||
"version": "1.3.8",
|
||||
"resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
|
||||
"integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew=="
|
||||
},
|
||||
"ipaddr.js": {
|
||||
"version": "1.9.0",
|
||||
@ -955,11 +1177,21 @@
|
||||
"resolved": "https://registry.npmjs.org/is-port-available/-/is-port-available-0.1.5.tgz",
|
||||
"integrity": "sha512-/r7UZAQtfgDFdhxzM71jG0mkC4oSRA513cImMILdRe/+UOIe0Se/D/Z7XCua4AFg5k4Zt3ALMGaC1W3FzlrR2w=="
|
||||
},
|
||||
"is-typedarray": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
|
||||
"integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo="
|
||||
},
|
||||
"isarray": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
|
||||
"integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
|
||||
},
|
||||
"isstream": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
|
||||
"integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo="
|
||||
},
|
||||
"jose": {
|
||||
"version": "1.15.1",
|
||||
"resolved": "https://registry.npmjs.org/jose/-/jose-1.15.1.tgz",
|
||||
@ -968,11 +1200,47 @@
|
||||
"asn1.js": "^5.2.0"
|
||||
}
|
||||
},
|
||||
"jsbn": {
|
||||
"version": "0.1.1",
|
||||
"resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
|
||||
"integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM="
|
||||
},
|
||||
"json-schema": {
|
||||
"version": "0.2.3",
|
||||
"resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz",
|
||||
"integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM="
|
||||
},
|
||||
"json-schema-traverse": {
|
||||
"version": "0.4.1",
|
||||
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
|
||||
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="
|
||||
},
|
||||
"json-stringify-safe": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
|
||||
"integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus="
|
||||
},
|
||||
"jsprim": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz",
|
||||
"integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=",
|
||||
"requires": {
|
||||
"assert-plus": "1.0.0",
|
||||
"extsprintf": "1.3.0",
|
||||
"json-schema": "0.2.3",
|
||||
"verror": "1.10.0"
|
||||
}
|
||||
},
|
||||
"lodash": {
|
||||
"version": "4.17.20",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz",
|
||||
"integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA=="
|
||||
},
|
||||
"lodash.assign": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.assign/-/lodash.assign-4.2.0.tgz",
|
||||
"integrity": "sha1-DZnzzNem0mHRm9rrkkUAXShYCOc="
|
||||
},
|
||||
"lodash.camelcase": {
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz",
|
||||
@ -988,6 +1256,14 @@
|
||||
"resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz",
|
||||
"integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA=="
|
||||
},
|
||||
"ltx": {
|
||||
"version": "2.10.0",
|
||||
"resolved": "https://registry.npmjs.org/ltx/-/ltx-2.10.0.tgz",
|
||||
"integrity": "sha512-RB4zR6Mrp/0wTNS9WxMvpgfht/7u/8QAC9DpPD19opL/4OASPa28uoliFqeDkLUU8pQ4aeAfATBZmz1aSAHkMw==",
|
||||
"requires": {
|
||||
"inherits": "^2.0.4"
|
||||
}
|
||||
},
|
||||
"make-error": {
|
||||
"version": "1.3.5",
|
||||
"resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.5.tgz",
|
||||
@ -1001,6 +1277,16 @@
|
||||
"make-error": "^1.3.5"
|
||||
}
|
||||
},
|
||||
"md5.js": {
|
||||
"version": "1.3.5",
|
||||
"resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz",
|
||||
"integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==",
|
||||
"requires": {
|
||||
"hash-base": "^3.0.0",
|
||||
"inherits": "^2.0.1",
|
||||
"safe-buffer": "^5.1.2"
|
||||
}
|
||||
},
|
||||
"media-typer": {
|
||||
"version": "0.3.0",
|
||||
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
|
||||
@ -1171,6 +1457,79 @@
|
||||
"tar": "^4.4.2"
|
||||
}
|
||||
},
|
||||
"node-xmpp-client": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/node-xmpp-client/-/node-xmpp-client-3.2.0.tgz",
|
||||
"integrity": "sha1-r0Un3wzFq9JpDLohOcwezcgeoYk=",
|
||||
"requires": {
|
||||
"browser-request": "^0.3.3",
|
||||
"debug": "^2.2.0",
|
||||
"md5.js": "^1.3.3",
|
||||
"minimist": "^1.2.0",
|
||||
"node-xmpp-core": "^5.0.9",
|
||||
"request": "^2.65.0",
|
||||
"ws": "^1.1.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"debug": {
|
||||
"version": "2.6.9",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
||||
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
|
||||
"requires": {
|
||||
"ms": "2.0.0"
|
||||
}
|
||||
},
|
||||
"ms": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
|
||||
},
|
||||
"ws": {
|
||||
"version": "1.1.5",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-1.1.5.tgz",
|
||||
"integrity": "sha512-o3KqipXNUdS7wpQzBHSe180lBGO60SoK0yVo3CYJgb2MkobuWuBX6dhkYP5ORCLd55y+SaflMOV5fqAB53ux4w==",
|
||||
"requires": {
|
||||
"options": ">=0.0.5",
|
||||
"ultron": "1.0.x"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"node-xmpp-core": {
|
||||
"version": "5.0.9",
|
||||
"resolved": "https://registry.npmjs.org/node-xmpp-core/-/node-xmpp-core-5.0.9.tgz",
|
||||
"integrity": "sha1-XCjCjtsfs/i+uixnYHd2E/SPNCo=",
|
||||
"requires": {
|
||||
"@xmpp/jid": "^0.0.2",
|
||||
"@xmpp/streamparser": "^0.0.6",
|
||||
"@xmpp/xml": "^0.1.3",
|
||||
"debug": "^2.2.0",
|
||||
"inherits": "^2.0.1",
|
||||
"lodash.assign": "^4.0.0",
|
||||
"node-xmpp-tls-connect": "^1.0.1",
|
||||
"reconnect-core": "https://github.com/dodo/reconnect-core/tarball/merged"
|
||||
},
|
||||
"dependencies": {
|
||||
"debug": {
|
||||
"version": "2.6.9",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
||||
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
|
||||
"requires": {
|
||||
"ms": "2.0.0"
|
||||
}
|
||||
},
|
||||
"ms": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
|
||||
}
|
||||
}
|
||||
},
|
||||
"node-xmpp-tls-connect": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/node-xmpp-tls-connect/-/node-xmpp-tls-connect-1.0.1.tgz",
|
||||
"integrity": "sha1-kazkOsJrE4hhsr5HjfnfGdYdxcM="
|
||||
},
|
||||
"nopt": {
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.3.tgz",
|
||||
@ -1236,6 +1595,11 @@
|
||||
"commander": "^3.0.2"
|
||||
}
|
||||
},
|
||||
"oauth-sign": {
|
||||
"version": "0.9.0",
|
||||
"resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz",
|
||||
"integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ=="
|
||||
},
|
||||
"object-assign": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
||||
@ -1262,6 +1626,11 @@
|
||||
"wrappy": "1"
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"version": "0.0.6",
|
||||
"resolved": "https://registry.npmjs.org/options/-/options-0.0.6.tgz",
|
||||
"integrity": "sha1-7CLTEoBrtT5zF3Pnza788cZDEo8="
|
||||
},
|
||||
"os-homedir": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz",
|
||||
@ -1317,6 +1686,11 @@
|
||||
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
|
||||
"integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w="
|
||||
},
|
||||
"performance-now": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
|
||||
"integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns="
|
||||
},
|
||||
"picomatch": {
|
||||
"version": "2.2.2",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz",
|
||||
@ -1342,6 +1716,21 @@
|
||||
"ipaddr.js": "1.9.0"
|
||||
}
|
||||
},
|
||||
"psl": {
|
||||
"version": "1.8.0",
|
||||
"resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz",
|
||||
"integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ=="
|
||||
},
|
||||
"punycode": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
|
||||
"integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A=="
|
||||
},
|
||||
"qbox": {
|
||||
"version": "0.1.7",
|
||||
"resolved": "https://registry.npmjs.org/qbox/-/qbox-0.1.7.tgz",
|
||||
"integrity": "sha1-6A8NxdCfhp2IghaMP2asjdKEDwI="
|
||||
},
|
||||
"qs": {
|
||||
"version": "6.7.0",
|
||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz",
|
||||
@ -1418,6 +1807,13 @@
|
||||
"picomatch": "^2.2.1"
|
||||
}
|
||||
},
|
||||
"reconnect-core": {
|
||||
"version": "https://github.com/dodo/reconnect-core/tarball/merged",
|
||||
"integrity": "sha512-wZK/v5ZaNaSUs2Wnwh2YSX/Jqv6bQHKNEwojdzV11tByKziR9ikOssf5tvUhx+8/oCBz6AakOFAjZuqPoiRHJQ==",
|
||||
"requires": {
|
||||
"backoff": "~2.3.0"
|
||||
}
|
||||
},
|
||||
"recursive-readdir": {
|
||||
"version": "2.2.2",
|
||||
"resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.2.tgz",
|
||||
@ -1426,6 +1822,40 @@
|
||||
"minimatch": "3.0.4"
|
||||
}
|
||||
},
|
||||
"request": {
|
||||
"version": "2.88.2",
|
||||
"resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz",
|
||||
"integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==",
|
||||
"requires": {
|
||||
"aws-sign2": "~0.7.0",
|
||||
"aws4": "^1.8.0",
|
||||
"caseless": "~0.12.0",
|
||||
"combined-stream": "~1.0.6",
|
||||
"extend": "~3.0.2",
|
||||
"forever-agent": "~0.6.1",
|
||||
"form-data": "~2.3.2",
|
||||
"har-validator": "~5.1.3",
|
||||
"http-signature": "~1.2.0",
|
||||
"is-typedarray": "~1.0.0",
|
||||
"isstream": "~0.1.2",
|
||||
"json-stringify-safe": "~5.0.1",
|
||||
"mime-types": "~2.1.19",
|
||||
"oauth-sign": "~0.9.0",
|
||||
"performance-now": "^2.1.0",
|
||||
"qs": "~6.5.2",
|
||||
"safe-buffer": "^5.1.2",
|
||||
"tough-cookie": "~2.5.0",
|
||||
"tunnel-agent": "^0.6.0",
|
||||
"uuid": "^3.3.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"qs": {
|
||||
"version": "6.5.2",
|
||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",
|
||||
"integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"rimraf": {
|
||||
"version": "2.7.1",
|
||||
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
|
||||
@ -1522,6 +1952,14 @@
|
||||
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz",
|
||||
"integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw=="
|
||||
},
|
||||
"shaka-player": {
|
||||
"version": "3.0.5",
|
||||
"resolved": "https://registry.npmjs.org/shaka-player/-/shaka-player-3.0.5.tgz",
|
||||
"integrity": "sha512-LYq56q9DA7yTLBD1yQwZrMlJZOovb2yRmo0C3AsddL1J0ee+U4BXr1QZd5amtpBvl8fOiLgkW/12UuChMR764A==",
|
||||
"requires": {
|
||||
"eme-encryption-scheme-polyfill": "^2.0.1"
|
||||
}
|
||||
},
|
||||
"signal-exit": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz",
|
||||
@ -1564,6 +2002,15 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"simple-xmpp": {
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/simple-xmpp/-/simple-xmpp-1.3.1.tgz",
|
||||
"integrity": "sha512-o0wGVlI8Q4o0qTz6Kylbo1QPOMVn+DA/vyHHZecqcQ+LK4ZWGe3wtRON9QjHAkSyxB36PoagmiUz4pHADau8Mw==",
|
||||
"requires": {
|
||||
"node-xmpp-client": "^3.0.0",
|
||||
"qbox": "0.1.x"
|
||||
}
|
||||
},
|
||||
"snekfetch": {
|
||||
"version": "3.6.4",
|
||||
"resolved": "https://registry.npmjs.org/snekfetch/-/snekfetch-3.6.4.tgz",
|
||||
@ -1742,6 +2189,29 @@
|
||||
"resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.1.tgz",
|
||||
"integrity": "sha1-R1OT/56RR5rqYtyvDKPRSYOn+0A="
|
||||
},
|
||||
"sshpk": {
|
||||
"version": "1.16.1",
|
||||
"resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz",
|
||||
"integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==",
|
||||
"requires": {
|
||||
"asn1": "~0.2.3",
|
||||
"assert-plus": "^1.0.0",
|
||||
"bcrypt-pbkdf": "^1.0.0",
|
||||
"dashdash": "^1.12.0",
|
||||
"ecc-jsbn": "~0.1.1",
|
||||
"getpass": "^0.1.1",
|
||||
"jsbn": "~0.1.0",
|
||||
"safer-buffer": "^2.0.2",
|
||||
"tweetnacl": "~0.14.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"tweetnacl": {
|
||||
"version": "0.14.5",
|
||||
"resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
|
||||
"integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q="
|
||||
}
|
||||
}
|
||||
},
|
||||
"statuses": {
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
|
||||
@ -1829,6 +2299,15 @@
|
||||
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz",
|
||||
"integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw=="
|
||||
},
|
||||
"tough-cookie": {
|
||||
"version": "2.5.0",
|
||||
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz",
|
||||
"integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==",
|
||||
"requires": {
|
||||
"psl": "^1.1.28",
|
||||
"punycode": "^2.1.1"
|
||||
}
|
||||
},
|
||||
"ts-node": {
|
||||
"version": "8.5.4",
|
||||
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-8.5.4.tgz",
|
||||
@ -1846,6 +2325,14 @@
|
||||
"resolved": "https://registry.npmjs.org/ts-toolbelt/-/ts-toolbelt-6.9.9.tgz",
|
||||
"integrity": "sha512-5a8k6qfbrL54N4Dw+i7M6kldrbjgDWb5Vit8DnT+gwThhvqMg8KtxLE5Vmnft+geIgaSOfNJyAcnmmlflS+Vdg=="
|
||||
},
|
||||
"tunnel-agent": {
|
||||
"version": "0.6.0",
|
||||
"resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
|
||||
"integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=",
|
||||
"requires": {
|
||||
"safe-buffer": "^5.0.1"
|
||||
}
|
||||
},
|
||||
"tweetnacl": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz",
|
||||
@ -1865,11 +2352,24 @@
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.6.3.tgz",
|
||||
"integrity": "sha512-N7bceJL1CtRQ2RiG0AQME13ksR7DiuQh/QehubYcghzv20tnh+MQnQIuJddTmsbqYj+dztchykemz0zFzlvdQw=="
|
||||
},
|
||||
"ultron": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/ultron/-/ultron-1.0.2.tgz",
|
||||
"integrity": "sha1-rOEWq1V80Zc4ak6I9GhTeMiy5Po="
|
||||
},
|
||||
"unpipe": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
|
||||
"integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw="
|
||||
},
|
||||
"uri-js": {
|
||||
"version": "4.4.0",
|
||||
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.0.tgz",
|
||||
"integrity": "sha512-B0yRTzYdUCCn9n+F4+Gh4yIDtMQcaJsmYBDsTSG8g/OejKBodLQ2IHfN3bM7jUsRXndopT7OIXWdYqc1fjmV6g==",
|
||||
"requires": {
|
||||
"punycode": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"util-deprecate": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||
@ -1880,11 +2380,26 @@
|
||||
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
|
||||
"integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM="
|
||||
},
|
||||
"uuid": {
|
||||
"version": "3.4.0",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
|
||||
"integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A=="
|
||||
},
|
||||
"vary": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
|
||||
"integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw="
|
||||
},
|
||||
"verror": {
|
||||
"version": "1.10.0",
|
||||
"resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz",
|
||||
"integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=",
|
||||
"requires": {
|
||||
"assert-plus": "^1.0.0",
|
||||
"core-util-is": "1.0.2",
|
||||
"extsprintf": "^1.2.0"
|
||||
}
|
||||
},
|
||||
"wide-align": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz",
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "satyr",
|
||||
"version": "0.10.0",
|
||||
"version": "1.0.0",
|
||||
"description": "A livestreaming server.",
|
||||
"license": "AGPL-3.0",
|
||||
"author": "knotteye",
|
||||
@ -10,11 +10,12 @@
|
||||
"setup": "sh install/setup.sh",
|
||||
"migrate": "ts-node src/migrate.ts",
|
||||
"invite": "ts-node src/cli.ts --invite",
|
||||
"v3-manual": "ts-node src/v3manual.ts",
|
||||
"make-templates": "nunjucks-precompile -i [\"\\.html$\",\"\\.njk$\"] templates > site/templates.js"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://gitlab.com/knotteye/satyr.git"
|
||||
"url": "https://git.waldn.net/git/knotteye/satyr.git"
|
||||
},
|
||||
"dependencies": {
|
||||
"bcrypt": "^5.0.0",
|
||||
@ -33,11 +34,13 @@
|
||||
"nunjucks": "^3.2.1",
|
||||
"parse-yaml": "^0.1.0",
|
||||
"recursive-readdir": "^2.2.2",
|
||||
"simple-xmpp": "^1.3.1",
|
||||
"socket-anti-spam": "^2.0.0",
|
||||
"socket.io": "^2.3.0",
|
||||
"strftime": "^0.10.0",
|
||||
"ts-node": "^8.5.4",
|
||||
"typescript": "^3.6.3"
|
||||
"typescript": "^3.6.3",
|
||||
"shaka-player": "^3.0.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^12.12.67"
|
||||
|
@ -1,14 +0,0 @@
|
||||
# dash.js BSD License Agreement
|
||||
|
||||
The copyright in this software is being made available under the BSD License, included below. This software may be subject to other third party and contributor rights, including patent rights, and no such rights are granted under this license.
|
||||
|
||||
**Copyright (c) 2015, Dash Industry Forum.
|
||||
**All rights reserved.**
|
||||
|
||||
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the Dash Industry Forum nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
||||
|
||||
**THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.**
|
||||
|
3
site/dashjs/dash.all.min.js
vendored
3
site/dashjs/dash.all.min.js
vendored
File diff suppressed because one or more lines are too long
@ -3,12 +3,13 @@
|
||||
<link rel="stylesheet" type="text/css" href="/styles.css">
|
||||
<link rel="stylesheet" type="text/css" href="/local.css">
|
||||
<link rel="icon" type="image/svg" href="/logo.svg">
|
||||
|
||||
<script src="/nunjucks-slim.js"></script>
|
||||
<script src="/templates.js"></script>
|
||||
<script src="/shaka-player.compiled.js"></script>
|
||||
|
||||
<script>
|
||||
nunjucks.configure({ autoescape: true });
|
||||
</script>
|
||||
<script>
|
||||
//should check for and refresh login tokens on pageload..
|
||||
if(document.cookie.match(/^(.*;)?\s*X-Auth-As\s*=\s*[^;]+(.*)?$/) !== null) {
|
||||
var xhr = new XMLHttpRequest();
|
||||
|
@ -1,5 +1,7 @@
|
||||
async function render(path){
|
||||
async function render(path, s){
|
||||
var context = await getContext();
|
||||
if(!s)
|
||||
history.pushState({}, context.sitename, location.protocol+'//'+location.host+path);
|
||||
switch(path){
|
||||
//nothing but context
|
||||
case (path.match(/^\/about\/?$/) || {}).input:
|
||||
@ -80,6 +82,7 @@ async function render(path){
|
||||
if(!config.title){document.body.innerHTML = nunjucks.render('404.njk', context); break;}
|
||||
document.body.innerHTML = nunjucks.render('user.njk', Object.assign({about: config.about, title: config.title, username: config.username}, context));
|
||||
modifyLinks();
|
||||
initPlayer(usr);
|
||||
break;
|
||||
case (path.match(/^\/vods\/.+\/manage\/?$/) || {}).input: // /vods/:user/manage
|
||||
var usr = path.substring(6, (path.length - 7));
|
||||
@ -99,10 +102,16 @@ async function render(path){
|
||||
//root
|
||||
case "/":
|
||||
render('/users/live');
|
||||
modifyLinks();
|
||||
break;
|
||||
case "":
|
||||
render('/users/live');
|
||||
modifyLinks();
|
||||
break;
|
||||
case "/index.html":
|
||||
render('/users/live');
|
||||
modifyLinks();
|
||||
break;
|
||||
//404
|
||||
default:
|
||||
document.body.innerHTML = nunjucks.render('404.njk', context);
|
||||
@ -110,6 +119,10 @@ async function render(path){
|
||||
}
|
||||
}
|
||||
|
||||
window.addEventListener('popstate', (event) => {
|
||||
render(document.location.pathname, true);
|
||||
});
|
||||
|
||||
async function getContext(){
|
||||
var info = JSON.parse(await makeRequest('GET', '/api/instance/info'));
|
||||
info.sitename = info.name;
|
||||
@ -157,7 +170,7 @@ function handleLoad() {
|
||||
|
||||
function modifyLinks() {
|
||||
for (var ls = document.links, numLinks = ls.length, i=0; i<numLinks; i++){
|
||||
if(ls[i].href.indexOf(location.protocol+'//'+location.host) !== -1) {
|
||||
if(ls[i].href.indexOf(location.protocol+'//'+location.host) !== -1 && ls[i].href.match(new RegExp(/\/\w+\.\w+$/)) === null) {
|
||||
//should be a regular link
|
||||
ls[i].setAttribute('onclick', 'return internalLink(\"'+ls[i].href.substring((location.protocol+'//'+location.host).length)+'\")');
|
||||
}
|
||||
@ -167,4 +180,55 @@ function modifyLinks() {
|
||||
function internalLink(path){
|
||||
this.render(path);
|
||||
return false;
|
||||
}
|
||||
|
||||
function sleep(ms) {
|
||||
return new Promise(resolve => setTimeout(resolve, ms));
|
||||
}
|
||||
|
||||
var shakaPolyFilled = false;
|
||||
async function initPlayer(usr) {
|
||||
var manifestUri = document.location.protocol+'//'+document.location.host+'/live/'+usr+'/index.mpd';
|
||||
if(!shakaPolyFilled){
|
||||
shaka.polyfill.installAll();
|
||||
shakaPolyFilled = true;
|
||||
}
|
||||
var live = JSON.parse(await makeRequest("GET", "/api/"+usr+"/config")).live;
|
||||
if(live){
|
||||
// Create a Player instance.
|
||||
const video = document.getElementById('video');
|
||||
const player = new shaka.Player(video);
|
||||
// Listen for error events.
|
||||
player.addEventListener('error', onErrorEvent);
|
||||
|
||||
video.addEventListener('play', () => {
|
||||
document.getElementById('playbtn').style.visibility = 'hidden';
|
||||
});
|
||||
video.addEventListener('pause', () => {
|
||||
document.getElementById('playbtn').style.visibility = 'visible';
|
||||
});
|
||||
// Try to load a manifest.
|
||||
// This is an asynchronous process.
|
||||
try {
|
||||
await player.load(manifestUri);
|
||||
// This runs if the asynchronous load is successful.
|
||||
console.log('The video has now been loaded!');
|
||||
} catch (e) {
|
||||
// onError is executed if the asynchronous load fails.
|
||||
onError(e);
|
||||
}
|
||||
} else {
|
||||
if(document.getElementById('video') !== null)
|
||||
setTimeout(initPlayer, 5000, usr);
|
||||
}
|
||||
}
|
||||
|
||||
function onErrorEvent(event) {
|
||||
// Extract the shaka.util.Error object from the event.
|
||||
onError(event.detail);
|
||||
}
|
||||
|
||||
function onError(error) {
|
||||
// Log the error.
|
||||
console.error('Error code', error.code, 'object', error);
|
||||
}
|
2
site/local.css
Normal file
2
site/local.css
Normal file
@ -0,0 +1,2 @@
|
||||
/* This file is for themeing Satyr's frontend without worrying about changes
|
||||
being overwritten. Feel free to make whatever local changes you want here. */
|
385
site/play.svg
Normal file
385
site/play.svg
Normal file
@ -0,0 +1,385 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
<svg
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns:ns1="http://sozi.baierouge.fr"
|
||||
id="svg1307"
|
||||
sodipodi:docname="media-playback-start.svg"
|
||||
inkscape:export-filename="/home/lapo/Desktop/Grafica/Icone/media-actions-outlines.png"
|
||||
viewBox="0 0 48 48"
|
||||
sodipodi:version="0.32"
|
||||
inkscape:export-xdpi="90"
|
||||
inkscape:output_extension="org.inkscape.output.svg.inkscape"
|
||||
inkscape:export-ydpi="90"
|
||||
inkscape:version="0.46"
|
||||
sodipodi:docbase="/home/jimmac/src/cvs/tango-icon-theme/scalable/actions"
|
||||
>
|
||||
<defs
|
||||
id="defs1309"
|
||||
>
|
||||
<linearGradient
|
||||
id="linearGradient2306"
|
||||
y2="95"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x2="70.827"
|
||||
gradientTransform="translate(-45 -71.094)"
|
||||
y1="124.12"
|
||||
x1="71.289"
|
||||
inkscape:collect="always"
|
||||
>
|
||||
<stop
|
||||
id="stop5077"
|
||||
style="stop-color:#adb0a8"
|
||||
offset="0"
|
||||
/>
|
||||
<stop
|
||||
id="stop5079"
|
||||
style="stop-color:#464744"
|
||||
offset="1"
|
||||
/>
|
||||
</linearGradient
|
||||
>
|
||||
<radialGradient
|
||||
id="radialGradient2314"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
cy="83.991"
|
||||
cx="107.59"
|
||||
gradientTransform="matrix(.053243 -.83624 2.0195 .12857 -151.92 108.08)"
|
||||
r="12.552"
|
||||
inkscape:collect="always"
|
||||
>
|
||||
<stop
|
||||
id="stop2693"
|
||||
style="stop-color:#ffffff"
|
||||
offset="0"
|
||||
/>
|
||||
<stop
|
||||
id="stop2695"
|
||||
style="stop-color:#d3d7cf"
|
||||
offset="1"
|
||||
/>
|
||||
</radialGradient
|
||||
>
|
||||
<linearGradient
|
||||
id="linearGradient2690"
|
||||
y2="88.924"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x2="70.952"
|
||||
gradientTransform="matrix(1.1282 0 0 1.1282 -53.993 -83.36)"
|
||||
y1="101.74"
|
||||
x1="70.914"
|
||||
inkscape:collect="always"
|
||||
>
|
||||
<stop
|
||||
id="stop2686"
|
||||
style="stop-color:#ffffff"
|
||||
offset="0"
|
||||
/>
|
||||
<stop
|
||||
id="stop2688"
|
||||
style="stop-color:#000000"
|
||||
offset="1"
|
||||
/>
|
||||
</linearGradient
|
||||
>
|
||||
</defs
|
||||
>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
inkscape:showpageshadow="false"
|
||||
inkscape:zoom="1"
|
||||
borderopacity="0.19607843"
|
||||
inkscape:current-layer="layer1"
|
||||
stroke="#555753"
|
||||
guidetolerance="1px"
|
||||
fill="#555753"
|
||||
inkscape:grid-points="true"
|
||||
inkscape:grid-bbox="true"
|
||||
showgrid="false"
|
||||
showguides="false"
|
||||
bordercolor="#666666"
|
||||
inkscape:window-x="326"
|
||||
inkscape:guide-bbox="true"
|
||||
inkscape:window-y="160"
|
||||
inkscape:window-width="872"
|
||||
inkscape:pageopacity="0.0000000"
|
||||
inkscape:pageshadow="2"
|
||||
pagecolor="#ffffff"
|
||||
inkscape:cx="-117.42449"
|
||||
inkscape:cy="12.980288"
|
||||
inkscape:document-units="px"
|
||||
inkscape:window-height="688"
|
||||
showborder="true"
|
||||
>
|
||||
<sodipodi:guide
|
||||
id="guide2194"
|
||||
position="38.996647"
|
||||
orientation="horizontal"
|
||||
/>
|
||||
<sodipodi:guide
|
||||
id="guide2196"
|
||||
position="9.0140845"
|
||||
orientation="horizontal"
|
||||
/>
|
||||
<sodipodi:guide
|
||||
id="guide2198"
|
||||
position="9.0140845"
|
||||
orientation="vertical"
|
||||
/>
|
||||
<sodipodi:guide
|
||||
id="guide2200"
|
||||
position="38.975184"
|
||||
orientation="vertical"
|
||||
/>
|
||||
<sodipodi:guide
|
||||
id="guide2202"
|
||||
position="22.988281"
|
||||
orientation="horizontal"
|
||||
/>
|
||||
<sodipodi:guide
|
||||
id="guide2204"
|
||||
position="23.908786"
|
||||
orientation="vertical"
|
||||
/>
|
||||
<sodipodi:guide
|
||||
id="guide4332"
|
||||
position="157.99417"
|
||||
orientation="vertical"
|
||||
/>
|
||||
<sodipodi:guide
|
||||
id="guide4334"
|
||||
position="-36.062446"
|
||||
orientation="horizontal"
|
||||
/>
|
||||
<sodipodi:guide
|
||||
id="guide4336"
|
||||
position="-58.02695"
|
||||
orientation="horizontal"
|
||||
/>
|
||||
<sodipodi:guide
|
||||
id="guide4338"
|
||||
position="180.00287"
|
||||
orientation="vertical"
|
||||
/>
|
||||
<sodipodi:guide
|
||||
id="guide4417"
|
||||
position="107.92217"
|
||||
orientation="vertical"
|
||||
/>
|
||||
<sodipodi:guide
|
||||
id="guide4419"
|
||||
position="129.93087"
|
||||
orientation="vertical"
|
||||
/>
|
||||
<sodipodi:guide
|
||||
id="guide5106"
|
||||
position="19.996875"
|
||||
orientation="horizontal"
|
||||
/>
|
||||
<sodipodi:guide
|
||||
id="guide5119"
|
||||
position="63.039674"
|
||||
orientation="horizontal"
|
||||
/>
|
||||
<sodipodi:guide
|
||||
id="guide5121"
|
||||
position="49.066305"
|
||||
orientation="horizontal"
|
||||
/>
|
||||
<sodipodi:guide
|
||||
id="guide5307"
|
||||
position="-86.007168"
|
||||
orientation="horizontal"
|
||||
/>
|
||||
<sodipodi:guide
|
||||
id="guide5309"
|
||||
position="-108.09009"
|
||||
orientation="horizontal"
|
||||
/>
|
||||
<sodipodi:guide
|
||||
id="guide3111"
|
||||
position="-100.15429"
|
||||
orientation="horizontal"
|
||||
/>
|
||||
<inkscape:grid
|
||||
id="GridFromPre046Settings"
|
||||
opacity=".2"
|
||||
color="#0000ff"
|
||||
originy="0px"
|
||||
originx="0px"
|
||||
empspacing="2"
|
||||
spacingy="0.5px"
|
||||
spacingx="0.5px"
|
||||
empopacity="0.4"
|
||||
type="xygrid"
|
||||
empcolor="#0000ff"
|
||||
/>
|
||||
</sodipodi:namedview
|
||||
>
|
||||
<g
|
||||
id="layer1"
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
>
|
||||
<path
|
||||
id="path2682"
|
||||
style="stroke-linejoin:round;opacity:.15;color:#000000;stroke:url(#linearGradient2690);stroke-linecap:square;stroke-width:2;fill:none"
|
||||
inkscape:r_cy="true"
|
||||
inkscape:r_cx="true"
|
||||
sodipodi:nodetypes="cccc"
|
||||
d="m12 39.5v-30.5l26.07 14.817-26.07 15.683z"
|
||||
/>
|
||||
<path
|
||||
id="path3375"
|
||||
style="fill-rule:evenodd;color:#000000;fill:url(#radialGradient2314)"
|
||||
inkscape:r_cy="true"
|
||||
inkscape:r_cx="true"
|
||||
sodipodi:nodetypes="cccc"
|
||||
d="m12.499 37.811v-27.811l24.104 13.906-24.104 13.905z"
|
||||
/>
|
||||
<path
|
||||
id="path2479"
|
||||
style="stroke-linejoin:round;color:#000000;stroke:url(#linearGradient2306);stroke-linecap:square;fill:none"
|
||||
inkscape:r_cy="true"
|
||||
inkscape:r_cx="true"
|
||||
sodipodi:nodetypes="cccc"
|
||||
d="m12.499 37.811v-27.811l24.104 13.906-24.104 13.905z"
|
||||
/>
|
||||
<path
|
||||
id="path2481"
|
||||
style="fill-rule:evenodd;color:#000000;fill:#ffffff"
|
||||
inkscape:r_cy="true"
|
||||
inkscape:r_cx="true"
|
||||
sodipodi:nodetypes="cccccccc"
|
||||
d="m12.999 10.874v26.063l22.594-13.031-22.594-13.032zm1 1.75l19.563 11.282-19.563 11.281v-22.563z"
|
||||
/>
|
||||
<path
|
||||
id="path2339"
|
||||
style="opacity:.5;color:#000000;display:block;fill:#ffffff"
|
||||
inkscape:r_cy="true"
|
||||
inkscape:r_cx="true"
|
||||
sodipodi:nodetypes="cccc"
|
||||
d="m13.938 12.562v11.688c4.269-0.045 9.164-0.346 17.062-1.875l-17.062-9.813z"
|
||||
/>
|
||||
</g
|
||||
>
|
||||
<metadata
|
||||
>
|
||||
<rdf:RDF
|
||||
>
|
||||
<cc:Work
|
||||
>
|
||||
<dc:format
|
||||
>image/svg+xml</dc:format
|
||||
>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage"
|
||||
/>
|
||||
<cc:license
|
||||
rdf:resource="http://creativecommons.org/licenses/publicdomain/"
|
||||
/>
|
||||
<dc:publisher
|
||||
>
|
||||
<cc:Agent
|
||||
rdf:about="http://openclipart.org/"
|
||||
>
|
||||
<dc:title
|
||||
>Openclipart</dc:title
|
||||
>
|
||||
</cc:Agent
|
||||
>
|
||||
</dc:publisher
|
||||
>
|
||||
<dc:title
|
||||
>tango media start</dc:title
|
||||
>
|
||||
<dc:date
|
||||
>2010-03-11T09:12:20</dc:date
|
||||
>
|
||||
<dc:description
|
||||
>"Play" or "start" icon from <A href="http://tango.freedesktop.org/Tango_Desktop_Project"> Tango Project </A> \n<BR><BR> \nSince version 0.8.90 Tango Project icons are Public Domain: <A href="http://tango.freedesktop.org/Frequently_Asked_Questions#Terms_of_Use.3F"> Tango Project FAQ </A></dc:description
|
||||
>
|
||||
<dc:source
|
||||
>https://openclipart.org/detail/31003/tango-media-start-by-warszawianka</dc:source
|
||||
>
|
||||
<dc:creator
|
||||
>
|
||||
<cc:Agent
|
||||
>
|
||||
<dc:title
|
||||
>warszawianka</dc:title
|
||||
>
|
||||
</cc:Agent
|
||||
>
|
||||
</dc:creator
|
||||
>
|
||||
<dc:subject
|
||||
>
|
||||
<rdf:Bag
|
||||
>
|
||||
<rdf:li
|
||||
>audio</rdf:li
|
||||
>
|
||||
<rdf:li
|
||||
>button</rdf:li
|
||||
>
|
||||
<rdf:li
|
||||
>externalsource</rdf:li
|
||||
>
|
||||
<rdf:li
|
||||
>icon</rdf:li
|
||||
>
|
||||
<rdf:li
|
||||
>play</rdf:li
|
||||
>
|
||||
<rdf:li
|
||||
>playback</rdf:li
|
||||
>
|
||||
<rdf:li
|
||||
>sign</rdf:li
|
||||
>
|
||||
<rdf:li
|
||||
>start</rdf:li
|
||||
>
|
||||
<rdf:li
|
||||
>symbol</rdf:li
|
||||
>
|
||||
<rdf:li
|
||||
>tango</rdf:li
|
||||
>
|
||||
<rdf:li
|
||||
>triangle</rdf:li
|
||||
>
|
||||
</rdf:Bag
|
||||
>
|
||||
</dc:subject
|
||||
>
|
||||
</cc:Work
|
||||
>
|
||||
<cc:License
|
||||
rdf:about="http://creativecommons.org/licenses/publicdomain/"
|
||||
>
|
||||
<cc:permits
|
||||
rdf:resource="http://creativecommons.org/ns#Reproduction"
|
||||
/>
|
||||
<cc:permits
|
||||
rdf:resource="http://creativecommons.org/ns#Distribution"
|
||||
/>
|
||||
<cc:permits
|
||||
rdf:resource="http://creativecommons.org/ns#DerivativeWorks"
|
||||
/>
|
||||
</cc:License
|
||||
>
|
||||
</rdf:RDF
|
||||
>
|
||||
</metadata
|
||||
>
|
||||
</svg
|
||||
>
|
After Width: | Height: | Size: 9.8 KiB |
@ -86,7 +86,7 @@ async function getConfig(username: string, all?: boolean): Promise<object>{
|
||||
if(all) {
|
||||
let users = await db.query('SELECT stream_key,record_flag FROM users WHERE username='+db.raw.escape(username));
|
||||
if(users[0]) Object.assign(t, users[0]);
|
||||
let usermeta = await db.query('SELECT title,about FROM user_meta WHERE username='+db.raw.escape(username));
|
||||
let usermeta = await db.query('SELECT title,about,live FROM user_meta WHERE username='+db.raw.escape(username));
|
||||
if(usermeta[0]) Object.assign(t, usermeta[0]);
|
||||
let ci = await db.query('SELECT irc,xmpp,twitch,discord FROM chat_integration WHERE username='+db.raw.escape(username));
|
||||
if(ci[0]) Object.assign(t, ci[0]);
|
||||
@ -94,7 +94,7 @@ async function getConfig(username: string, all?: boolean): Promise<object>{
|
||||
if(tw[0]) t['twitch_mirror'] = Object.assign({}, tw[0]);
|
||||
}
|
||||
else {
|
||||
let um = await db.query('SELECT title,about FROM user_meta WHERE username='+db.raw.escape(username));
|
||||
let um = await db.query('SELECT title,about,live FROM user_meta WHERE username='+db.raw.escape(username));
|
||||
if(um[0]) Object.assign(t, um[0]);
|
||||
}
|
||||
return t;
|
||||
|
51
src/chat.ts
51
src/chat.ts
@ -4,9 +4,12 @@ import {io} from "./http";
|
||||
import * as irc from "irc";
|
||||
import * as discord from "discord.js";
|
||||
import * as twitch from "dank-twitch-irc";
|
||||
import * as xmpp from "simple-xmpp";
|
||||
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));
|
||||
|
||||
var ircClient;
|
||||
var xmppClient;
|
||||
var xmppIgnore: Array<string> = [];
|
||||
var xmppJoined: Array<string> = [];
|
||||
var twitchClient;
|
||||
var twitchArr: Array<string> = [];
|
||||
var discordClient;
|
||||
@ -51,7 +54,24 @@ async function init() {
|
||||
});
|
||||
}
|
||||
if(config['chat']['xmpp']['enabled']){
|
||||
|
||||
xmpp.on('online', (data) => {
|
||||
console.log("XMPP Client Ready");
|
||||
});
|
||||
xmpp.on('groupchat', function(conference, from, message, stamp) {
|
||||
if(xmppIgnore.findIndex((e) => { return e === conference }) !== -1) return false;
|
||||
if(from === config['chat']['xmpp']['nickname']) return false;
|
||||
console.log(from+'\n'+conference+'\n'+message+'\n'+stamp);
|
||||
var lu = getUsr(conference, "xmpp");
|
||||
for(var i=0;i<lu.length;i++){
|
||||
sendAll(lu[i], [from, message], "xmpp")
|
||||
}
|
||||
});
|
||||
xmpp.connect({
|
||||
jid: config['chat']['xmpp']['jid'],
|
||||
password: config['chat']['xmpp']['password'],
|
||||
host: config['chat']['xmpp']['server'],
|
||||
port: config['chat']['xmpp']['port']
|
||||
});
|
||||
}
|
||||
if(config['chat']['twitch']['enabled']){
|
||||
twitchClient = new twitch.ChatClient({
|
||||
@ -96,12 +116,14 @@ async function updateInteg() {
|
||||
chatIntegration = [];
|
||||
if(config['chat']['irc']['enabled']) updateIRCChan();
|
||||
if(config['chat']['twitch']['enabled']) updateTwitchChan();
|
||||
if(config['chat']['xmpp']['enabled']) updateXmppChan();
|
||||
return;
|
||||
}
|
||||
if(liveUsers.length === 1) {
|
||||
chatIntegration = await db.query('SELECT * FROM chat_integration WHERE username='+db.raw.escape(liveUsers[0]['username']));
|
||||
if(config['chat']['irc']['enabled']) updateIRCChan();
|
||||
if(config['chat']['twitch']['enabled']) updateTwitchChan();
|
||||
if(config['chat']['xmpp']['enabled']) updateXmppChan();
|
||||
return;
|
||||
}
|
||||
var qs: string;
|
||||
@ -112,6 +134,7 @@ async function updateInteg() {
|
||||
chatIntegration = await db.query('SELECT * FROM chat_integration WHERE username='+qs);
|
||||
if(config['chat']['irc']['enabled']) updateIRCChan();
|
||||
if(config['chat']['twitch']['enabled']) updateTwitchChan();
|
||||
if(config['chat']['xmpp']['enabled']) updateXmppChan();
|
||||
}
|
||||
|
||||
async function sendAll(user: string, msg: Array<string>, src: string) {
|
||||
@ -126,7 +149,7 @@ async function sendAll(user: string, msg: Array<string>, src: string) {
|
||||
if(src !== "irc") sendIRC(getCh(user, "irc"), '['+src.toUpperCase()+']'+msg[0]+': '+msg[1]);
|
||||
if(src !== "twitch") sendTwitch(getCh(user, "twitch"), '['+src.toUpperCase()+']'+msg[0]+': '+msg[1]);
|
||||
if(src !== "discord") sendDiscord(getCh(user, "discord"), '['+src.toUpperCase()+']'+msg[0]+': '+msg[1]);
|
||||
//if(src !== "xmpp") sendXMPP();
|
||||
if(src !== "xmpp") sendXMPP(getCh(user, "xmpp"), '['+src.toUpperCase()+']'+msg[0]+': '+msg[1]);
|
||||
if(src !== "web") sendWeb(user, ['['+src.toUpperCase()+']'+msg[0], msg[1]]);
|
||||
}
|
||||
|
||||
@ -146,6 +169,7 @@ async function sendDiscord(channel: string, msg: string) {
|
||||
async function sendXMPP(channel: string, msg: string) {
|
||||
if(!config['chat']['xmpp']['enabled']) return;
|
||||
if(channel === null) return;
|
||||
xmpp.send(channel, msg, true);
|
||||
}
|
||||
|
||||
async function sendTwitch(channel: string, msg: string) {
|
||||
@ -252,4 +276,25 @@ async function normalizeDiscordMsg(msg): Promise<string>{
|
||||
return nmsg;
|
||||
}
|
||||
|
||||
function xmppJoin(room: string): void{
|
||||
if(xmppJoined.findIndex((e) => { return e === room }) !== -1) return;
|
||||
var stanza = new xmpp.Element('presence', {"to": room+'/'+config['chat']['xmpp']['nickname']}).c('x', { xmlns: 'http://jabber.org/protocol/muc' }).c('history', { maxstanzas: 0, seconds: 0});
|
||||
xmpp.conn.send(stanza);
|
||||
xmppIgnore = xmppIgnore.concat([room]);
|
||||
xmpp.join(room+'/'+config['chat']['xmpp']['nickname']);
|
||||
xmppJoined = xmppJoined.concat([room]);
|
||||
sleep(4000).then(() => {
|
||||
xmppIgnore = xmppIgnore.filter((item) => {
|
||||
return item !== room;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function updateXmppChan(): void{
|
||||
for(var i=0;i<chatIntegration.length;i++){
|
||||
if(chatIntegration[i]['xmpp'].trim() !== "" && chatIntegration[i]['xmpp'] !== null) xmppJoin(chatIntegration[i]['xmpp']);
|
||||
}
|
||||
//we can't really leave channels so I'll come back to that.
|
||||
}
|
||||
|
||||
export { init, sendAll };
|
@ -9,11 +9,8 @@ async function init() {
|
||||
if(tmp.length === 0){
|
||||
console.log('No database version info, running initial migration.');
|
||||
await require('./db/0').run();
|
||||
await bringUpToDate();
|
||||
}
|
||||
else {
|
||||
await bringUpToDate();
|
||||
}
|
||||
await bringUpToDate();
|
||||
}
|
||||
else {
|
||||
console.log('Skipping database version check.');
|
||||
@ -34,16 +31,19 @@ async function init() {
|
||||
}
|
||||
|
||||
async function bringUpToDate(): Promise<void>{
|
||||
var versions: Object[] = await db.query('select * from db_meta');
|
||||
var scripts: Buffer[] | string[] = readdirSync('./src/db/', {withFileTypes: false});
|
||||
var diff: number = scripts.length - versions.length
|
||||
if(diff === 0){
|
||||
var versions: Object[] = JSON.parse(JSON.stringify(await db.query('select * from db_meta'))); //ugh, don't ask
|
||||
var scripts: any[] = readdirSync('./src/db/', {withFileTypes: false});
|
||||
if(scripts.length - versions.length === 0){
|
||||
console.log('No migration needed.');
|
||||
} else {
|
||||
console.log('Versions differ, migrating now.');
|
||||
for(let i=0;i<diff;i++){
|
||||
console.log('Migration to version '+Math.floor(scripts.length-(diff-i)));
|
||||
await require('./db/'+scripts[Math.floor(scripts.length-(diff-i))]).run();
|
||||
var diff: string[] = scripts.filter(n => {
|
||||
//we have to use versions.some because {version: 0} === {version: 0} returns false lmao
|
||||
return !versions.some(o => o['version']+''=== n.substring(0, n.length - 3))
|
||||
});
|
||||
for(let i=0;i<diff.length;i++){
|
||||
console.log('Running migration '+diff[i]);
|
||||
await require('./db/'+diff[i]).run();
|
||||
}
|
||||
console.log('Done migrating database.');
|
||||
}
|
||||
|
@ -133,14 +133,14 @@ if (cluster.isMaster) {
|
||||
db.query('update user_meta set live=true where username=\''+results[0].username+'\' limit 1');
|
||||
db.query('SELECT twitch_key,enabled from twitch_mirror where username='+db.raw.escape(results[0].username)+' limit 1').then(async (tm) => {
|
||||
if(!tm[0]['enabled'] || !config['twitch_mirror']['enabled'] || !config['twitch_mirror']['ingest']) return;
|
||||
console.log('[NodeMediaServer] Mirroring to twitch for stream:',id)
|
||||
console.log(`[RTMP Cluster WORKER ${process.pid}] Mirroring to twitch for stream: ${id}`)
|
||||
execFile(config['media']['ffmpeg'], ['-loglevel', 'fatal', '-i', 'rtmp://127.0.0.1:'+wPort+'/'+config['media']['privateEndpoint']+'/'+key, '-vcodec', 'copy', '-acodec', 'copy', '-f', 'flv', config['twitch_mirror']['ingest']+tm[0]['twitch_key']], {
|
||||
detached: true,
|
||||
stdio : 'inherit',
|
||||
maxBuffer: Infinity
|
||||
}).unref();
|
||||
});
|
||||
console.log('[NodeMediaServer] Stream key ok for stream:',id);
|
||||
console.log(`[RTMP Cluster WORKER ${process.pid}] Stream key ok for stream: ${id}`);
|
||||
console.log(`[RTMP Cluster WORKER ${process.pid}] Stream key ok for stream: ${id}`);
|
||||
//notify master process that we're handling the stream for this user
|
||||
process.send({type: 'handle-publish', name:results[0].username});
|
||||
@ -171,14 +171,14 @@ if (cluster.isMaster) {
|
||||
let key: string = StreamPath.split("/")[2];
|
||||
//correctly formatted urls again
|
||||
if (StreamPath.split("/").length !== 3){
|
||||
console.log("[NodeMediaServer] Malformed URL, closing connection for stream:",id);
|
||||
console.log(`[RTMP Cluster WORKER ${process.pid}] Malformed URL, closing connection for stream: ${id}`);
|
||||
session.reject();
|
||||
return false;
|
||||
}
|
||||
//localhost can play from whatever endpoint
|
||||
//other clients must use private endpoint
|
||||
if(app !== config['media']['publicEndpoint'] && !session.isLocal) {
|
||||
console.log("[NodeMediaServer] Non-local Play from private endpoint, rejecting client:",id);
|
||||
console.log(`[RTMP Cluster WORKER ${process.pid}] Non-local Play from private endpoint, rejecting client: ${id}`);
|
||||
session.reject();
|
||||
return false;
|
||||
}
|
||||
|
@ -4,7 +4,17 @@ try {
|
||||
var localconfig: Object = parse(read('config/config.yml'));
|
||||
console.log('Config file found.');
|
||||
} catch (e) {
|
||||
console.log('No config file found. Exiting.');
|
||||
if(e['reason']) {
|
||||
console.log('Error parsing config on line '+e['mark']['line']+', with reason: '+e['reason']);
|
||||
}
|
||||
else {
|
||||
console.log('Config Error: '+e['code']);
|
||||
switch(e['code']){
|
||||
case 'ENOENT':
|
||||
console.log('Does the file exist?');
|
||||
break;
|
||||
}
|
||||
}
|
||||
process.exit();
|
||||
}
|
||||
const config: Object = {
|
||||
@ -29,7 +39,13 @@ const config: Object = {
|
||||
connectionTimeout: '1000',
|
||||
insecureAuth: false,
|
||||
debug: false }, localconfig['database']),
|
||||
hwaccel: Object.assign({
|
||||
type: null,
|
||||
device: null,
|
||||
decode: false
|
||||
}, localconfig['hwaccel']),
|
||||
rtmp: Object.assign({
|
||||
cluster: false,
|
||||
port: 1935,
|
||||
chunk_size: 6000,
|
||||
gop_cache: true,
|
||||
@ -75,8 +91,9 @@ const config: Object = {
|
||||
enabled: false,
|
||||
server: null,
|
||||
port: 5222,
|
||||
nickname: 'SatyrChat',
|
||||
username: 'SatyrChat'
|
||||
jid: null,
|
||||
password: null,
|
||||
nickname: 'SatyrChat'
|
||||
}, localconfig['chat']['xmpp']),
|
||||
|
||||
twitch: Object.assign({
|
||||
|
@ -27,7 +27,7 @@ async function addUser(name: string, password: string){
|
||||
let dupe = await query('select * from users where username='+raw.escape(name));
|
||||
if(dupe[0]) return false;
|
||||
await query('INSERT INTO users (username, password_hash, stream_key, record_flag) VALUES ('+raw.escape(name)+', '+raw.escape(hash)+', '+raw.escape(key)+', 0)');
|
||||
await query('INSERT INTO user_meta (username, title, about, live) VALUES ('+raw.escape(name)+',\'\',\'\',false)');
|
||||
await query('INSERT INTO user_meta (username, title, about, live) VALUES ('+raw.escape(name)+',\' \',\' \',false)');
|
||||
await query('INSERT INTO chat_integration (username, irc, xmpp, twitch, discord) VALUES ('+raw.escape(name)+',\'\',\'\',\'\',\'\')');
|
||||
await query('INSERT INTO twitch_mirror (username) VALUES ('+raw.escape(name)+')');
|
||||
return true;
|
||||
@ -40,6 +40,7 @@ async function rmUser(name: string){
|
||||
await query('delete from user_meta where username='+raw.escape(name)+' limit 1');
|
||||
await query('delete from chat_integration where username='+raw.escape(name)+' limit 1');
|
||||
await query('delete from twitch_mirror where username='+raw.escape(name)+' limit 1');
|
||||
await query('delete from ch_bans where channel='+raw.escape(name));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
21
src/db/3.ts
Normal file
21
src/db/3.ts
Normal file
@ -0,0 +1,21 @@
|
||||
import * as db from "../database";
|
||||
import * as dirty from "dirty";
|
||||
|
||||
async function run () {
|
||||
await db.query('CREATE TABLE IF NOT EXISTS ch_bans(channel VARCHAR(25), target VARCHAR(45), time BIGINT, length INT DEFAULT 30)');
|
||||
console.log('!!! This migration has a race condition when run from the `npm run migrate` command. If thats how this was called, please re-run this migration manually.\n!!! Run `npm run v3-manual` to do so');
|
||||
var bansdb = new dirty('./config/bans.db')
|
||||
bansdb.on('load', async () => {
|
||||
bansdb.forEach(async (key, val) => {
|
||||
let ips = Object.keys(val);
|
||||
for(var i=0;i<ips.length;i++){
|
||||
await db.query('INSERT INTO ch_bans (channel, target, time, length) VALUES ('+db.raw.escape(key)+', '+db.raw.escape(ips[i])+', '+val[ips[i]].time+', '+val[ips[i]].length+')');
|
||||
}
|
||||
});
|
||||
await db.query('INSERT INTO db_meta (version) VALUES (3)');
|
||||
console.log('Done migrating bans.db');
|
||||
console.log('If this was a manual migration, you can kill the process now.');
|
||||
});
|
||||
}
|
||||
|
||||
export { run }
|
51
src/http.ts
51
src/http.ts
@ -17,7 +17,6 @@ const app = express();
|
||||
const server = http.createServer(app);
|
||||
const io = socketio(server);
|
||||
const store = dirty();
|
||||
var banlist;
|
||||
var jwkey;
|
||||
try{
|
||||
jwkey = JWK.asKey(readFileSync('./config/jwt.pem'));
|
||||
@ -59,6 +58,13 @@ async function init(){
|
||||
await initAPI();
|
||||
//static files if nothing else matches
|
||||
app.use(express.static(config['http']['directory']));
|
||||
//Fake static files
|
||||
app.get('/shaka-player.compiled.js', (req, res) => {
|
||||
res.sendFile(process.cwd()+'/node_modules/shaka-player/dist/shaka-player.compiled.js');
|
||||
});
|
||||
app.get('/shaka-player.compiled.map', (req, res) => {
|
||||
res.sendFile(process.cwd()+'/node_modules/shaka-player/dist/shaka-player.compiled.map');
|
||||
});
|
||||
//client-side site routes
|
||||
if(!config['http']['server_side_render'])
|
||||
await initFE();
|
||||
@ -70,7 +76,7 @@ async function init(){
|
||||
else res.status(404).render('404.njk', njkconf);
|
||||
//res.status(404).render('404.njk', njkconf);
|
||||
});
|
||||
banlist = new dirty('./config/bans.db').on('load', () => {initChat()});
|
||||
await initChat();
|
||||
server.listen(config['http']['port']);
|
||||
}
|
||||
|
||||
@ -168,6 +174,7 @@ async function initAPI() {
|
||||
app.get('/api/instance/config', (req, res) => {
|
||||
res.json({
|
||||
rtmp: {
|
||||
cluster: config['rtmp']['cluster'],
|
||||
port: config['rtmp']['port'],
|
||||
ping_timeout: config['rtmp']['ping_timeout']
|
||||
},
|
||||
@ -246,6 +253,7 @@ async function initAPI() {
|
||||
api.register(req.body.username, req.body.password, req.body.confirm, true).then((result) => {
|
||||
if(result[0]) return genToken(req.body.username).then((t) => {
|
||||
res.cookie('Authorization', t, {maxAge: 604800000, httpOnly: true, sameSite: 'Lax'});
|
||||
res.cookie('X-Auth-As', req.body.username, {maxAge: 604800000, httpOnly: false, sameSite: 'Lax'})
|
||||
res.json(result);
|
||||
api.useInvite(req.body.invite);
|
||||
return;
|
||||
@ -260,6 +268,7 @@ async function initAPI() {
|
||||
api.register(req.body.username, req.body.password, req.body.confirm).then( (result) => {
|
||||
if(result[0]) return genToken(req.body.username).then((t) => {
|
||||
res.cookie('Authorization', t, {maxAge: 604800000, httpOnly: true, sameSite: 'Lax'});
|
||||
res.cookie('X-Auth-As', req.body.username, {maxAge: 604800000, httpOnly: false, sameSite: 'Lax'})
|
||||
res.json(result);
|
||||
return;
|
||||
});
|
||||
@ -581,9 +590,10 @@ async function initChat() {
|
||||
socket.on('JOINROOM', async (data) => {
|
||||
let t: any = await db.query('select username from users where username='+db.raw.escape(data));
|
||||
if(t[0]){
|
||||
if(banlist.get(data) && banlist.get(data)[socket['handshake']['address']]){
|
||||
if(Math.floor(banlist.get(data)[socket['handshake']['address']]['time'] + (banlist.get(data)[socket['handshake']['address']]['length'] * 60)) < Math.floor(Date.now() / 1000)){
|
||||
banlist.set(data, Object.assign({}, banlist.get(data), {[socket['handshake']['address']]: null}));
|
||||
let b = await db.query('select * from ch_bans where target='+db.raw.escape(socket['handshake']['address'])+' and channel='+db.raw.escape(data));
|
||||
if(b[0]){
|
||||
if(Math.floor(b[0].time + (b[0].length * 60)) < Math.floor(Date.now() / 1000)){
|
||||
await db.query('delete from ch_bans where target='+db.raw.escape(b[0].target)+'and channel='+db.raw.escape(b[0].channel)+' and time='+db.raw.escape(b[0].time)+' and length='+db.raw.escape(b[0].length));
|
||||
}
|
||||
else {
|
||||
socket.emit('ALERT', 'You are banned from that room');
|
||||
@ -672,23 +682,27 @@ async function initChat() {
|
||||
}
|
||||
else socket.emit('ALERT', 'Not authorized to do that.');
|
||||
});
|
||||
socket.on('BAN', (data: Object) => {
|
||||
socket.on('BAN', async (data: Object) => {
|
||||
if(socket.nick === data['room']){
|
||||
let id: string = store.get(data['nick']);
|
||||
if(id){
|
||||
if(Array.isArray(id)) {
|
||||
for(let i=0;i<id.length;i++){
|
||||
let target = io.sockets.connected[id[i]];
|
||||
if(typeof(data['time']) === 'number' && (data['time'] !== 0 && data['time'] !== NaN)) banlist.set(data['room'], Object.assign({}, banlist.get(data['room']), {[target.ip]: {time: Math.floor(Date.now() / 1000), length: data['time']}}));
|
||||
else banlist.set(data['room'], Object.assign({}, banlist.get(data['room']), {[target.ip]: {time: Math.floor(Date.now() / 1000), length: 30}}));
|
||||
if(typeof(data['time']) === 'number' && (data['time'] !== 0 && data['time'] !== NaN))
|
||||
await db.query('insert into ch_bans (channel, target, time, length) VALUES ('+db.raw.escape(data['room'])+', '+db.raw.escape(target.ip)+', '+db.raw.escape(Math.floor(Date.now() / 1000))+', '+db.raw.escape(data['time'])+')');
|
||||
else
|
||||
await db.query('insert into ch_bans (channel, target, time, length) VALUES ('+db.raw.escape(data['room'])+', '+db.raw.escape(target.ip)+', '+db.raw.escape(Math.floor(Date.now() / 1000))+', '+db.raw.escape(30)+')');
|
||||
target.leave(data['room']);
|
||||
}
|
||||
io.to(data['room']).emit('ALERT', data['nick']+' was banned.');
|
||||
return;
|
||||
}
|
||||
let target = io.sockets.connected[id];
|
||||
if(typeof(data['time']) === 'number' && (data['time'] !== 0 && data['time'] !== NaN)) banlist.set(data['room'], Object.assign({}, banlist.get(data['room']), {[target.ip]: {time: Math.floor(Date.now() / 1000), length: data['time']}}));
|
||||
else banlist.set(data['room'], Object.assign({}, banlist.get(data['room']), {[target.ip]: {time: Math.floor(Date.now() / 1000), length: 30}}));
|
||||
if(typeof(data['time']) === 'number' && (data['time'] !== 0 && data['time'] !== NaN))
|
||||
await db.query('insert into ch_bans (channel, target, time, length) VALUES ('+db.raw.escape(data['room'])+', '+db.raw.escape(target.ip)+', '+db.raw.escape(Math.floor(Date.now() / 1000))+', '+db.raw.escape(data['time'])+')');
|
||||
else
|
||||
await db.query('insert into ch_bans (channel, target, time, length) VALUES ('+db.raw.escape(data['room'])+', '+db.raw.escape(target.ip)+', '+db.raw.escape(Math.floor(Date.now() / 1000))+', '+db.raw.escape(30)+')');
|
||||
target.leave(data['room']);
|
||||
io.to(data['room']).emit('ALERT', target.nick+' was banned.');
|
||||
}
|
||||
@ -696,10 +710,11 @@ async function initChat() {
|
||||
}
|
||||
else socket.emit('ALERT', 'Not authorized to do that.');
|
||||
});
|
||||
socket.on('UNBAN', (data: Object) => {
|
||||
socket.on('UNBAN', async (data: Object) => {
|
||||
if(socket.nick === data['room']){
|
||||
if(banlist.get(data['room']) && banlist.get(data['room'])[data['ip']]){
|
||||
banlist.set(data['room'], Object.assign({}, banlist.get(data['room']), {[data['ip']]: null}));
|
||||
let b = await db.query('select * from ch_bans where channel='+db.raw.escape(data['room'])+' and target='+db.raw.escape(data['ip']));
|
||||
if(b[0]){
|
||||
await db.query('delete from ch_bans where channel='+db.raw.escape(data['room'])+' and target='+db.raw.escape(data['ip']));
|
||||
socket.emit('ALERT', data['ip']+' was unbanned.');
|
||||
}
|
||||
else
|
||||
@ -707,13 +722,13 @@ async function initChat() {
|
||||
}
|
||||
else socket.emit('ALERT', 'Not authorized to do that.');
|
||||
});
|
||||
socket.on('LISTBAN', (data: Object) => {
|
||||
socket.on('LISTBAN', async (data: Object) => {
|
||||
if(socket.nick === data['room']){
|
||||
if(banlist.get(data['room'])) {
|
||||
let bans = Object.keys(banlist.get(data['room']));
|
||||
let b = await db.query('select target from ch_bans where channel='+db.raw.escape(data['room']));
|
||||
if(b[0]) {
|
||||
let str = '';
|
||||
for(let i=0;i<bans.length;i++){
|
||||
str += bans[i]+', ';
|
||||
for(let i=0;i<b.length;i++){
|
||||
str += b[i].target+', ';
|
||||
}
|
||||
socket.emit('ALERT', 'Banned IP adresses: '+str.substring(0, str.length - 2));
|
||||
return;
|
||||
|
@ -10,7 +10,7 @@ async function run() {
|
||||
await initDB();
|
||||
await clean();
|
||||
await initHTTP();
|
||||
config['rtmp']['cluster'] ? execFile(process.cwd()+'/node_modules/.bin/ts-node' [process.cwd()+'src/cluster.ts']) : await initRTMP();
|
||||
config['rtmp']['cluster'] ? execFile(process.cwd()+'/node_modules/.bin/ts-node', [process.cwd()+'/src/cluster.ts']) : await initRTMP();
|
||||
await initChat();
|
||||
console.log(`Satyr v${config['satyr']['version']} ready`);
|
||||
}
|
||||
|
@ -3,7 +3,8 @@ import {init as clean} from "./cleanup";
|
||||
import { config } from "./config";
|
||||
|
||||
async function run() {
|
||||
process.argv = process.argv.concat(['--skip-compile']);
|
||||
await initDB();
|
||||
await clean(false);
|
||||
await clean();
|
||||
}
|
||||
run().then(() => {process.exit()});
|
||||
run().then(() => {process.exit()});
|
||||
|
@ -42,9 +42,10 @@ function init () {
|
||||
if(session.audioCodec !== 0 && session.videoCodec !== 0){
|
||||
transCommand(results[0].username, key).then((r) => {
|
||||
execFile(config['media']['ffmpeg'], r, {maxBuffer: Infinity}, (err, stdout, stderr) => {
|
||||
/*console.log(err);
|
||||
console.log(stdout);
|
||||
console.log(stderr);*/
|
||||
//console.log(r);
|
||||
//console.log(err);
|
||||
//console.log(stdout);
|
||||
//console.log(stderr);
|
||||
});
|
||||
});
|
||||
break;
|
||||
@ -126,29 +127,51 @@ function init () {
|
||||
|
||||
async function transCommand(user: string, key: string): Promise<string[]>{
|
||||
let args: string[] = ['-loglevel', 'fatal', '-y'];
|
||||
let vcodec: string = 'libx264';
|
||||
if(config['hwaccel']['type'] === 'nvenc'){
|
||||
vcodec = 'h264_nvenc';
|
||||
if(config['hwaccel']['decode']){
|
||||
args = args.concat(['-hwaccel', 'cuda']);
|
||||
if(config['hwaccel']['device'])
|
||||
args = args.concat(['-hwaccel_device', config['hwaccel']['device']]);
|
||||
args = args.concat(['-hwaccel_output_format', 'cuda']);
|
||||
}
|
||||
}
|
||||
else if (config['hwaccel']['type'] === 'vaapi') {
|
||||
vcodec = 'h264_vaapi';
|
||||
if(config['hwaccel']['decode'] === 'fallback'){
|
||||
args = args.concat('init_hw_device', 'vaapi=foo:'+config['hwaccel']['device'], '-hwaccel vaapi', '-hwaccel_output_format', 'vaapi', '-hwaccel_device', 'foo');
|
||||
} else if (config['hwaccel']['decode']) {
|
||||
args = args.concat(['-hwaccel', 'vaapi', '-hwaccel_output_format', 'vaapi', '-vaapi_device', config['hwaccel']['device']]);
|
||||
}
|
||||
}
|
||||
if(config['transcode']['inputflags'] !== null && config['transcode']['inputflags'] !== "") args = args.concat(config['transcode']['inputflags'].split(" "));
|
||||
args = args.concat(['-i', 'rtmp://127.0.0.1:'+config['rtmp']['port']+'/'+config['media']['privateEndpoint']+'/'+key, '-movflags', '+faststart']);
|
||||
if(config['transcode']['adaptive']===true && config['transcode']['variants'] > 1) {
|
||||
for(let i=0;i<config['transcode']['variants'];i++){
|
||||
args = args.concat(['-map', '0:2']);
|
||||
args = args.concat(['-map', '0:v']);
|
||||
}
|
||||
args = args.concat(['-map', '0:1', '-c:a', 'aac', '-c:v:0', 'libx264']);
|
||||
args = args.concat(['-map', '0:a', '-c:a', 'aac', '-c:v:0', vcodec]);
|
||||
for(let i=1;i<config['transcode']['variants'];i++){
|
||||
args = args.concat(['-c:v:'+i, 'libx264',]);
|
||||
args = args.concat(['-c:v:'+i, vcodec,]);
|
||||
}
|
||||
if(!config['hwaccel']['type']){
|
||||
for(let i=1;i<config['transcode']['variants'];i++){
|
||||
let crf: number = Math.floor(18 + (i * 8)) > 51 ? 51 : Math.floor(18 + (i * 7));
|
||||
args = args.concat(['-crf:'+i, ''+crf]);
|
||||
}
|
||||
}
|
||||
for(let i=1;i<config['transcode']['variants'];i++){
|
||||
let crf: number = Math.floor(18 + (i * 8)) > 51 ? 51 : Math.floor(18 + (i * 7));
|
||||
args = args.concat(['-crf:'+i, ''+crf]);
|
||||
}
|
||||
for(let i=1;i<config['transcode']['variants'];i++){
|
||||
let bv: number = Math.floor((5000 / config['transcode']['variants']) * (config['transcode']['variants'] - i));
|
||||
let bv: number = Math.floor((10000 / config['transcode']['variants']) * (config['transcode']['variants'] - i));
|
||||
args = args.concat(['-b:v:'+i, ''+bv]);
|
||||
}
|
||||
}
|
||||
else {
|
||||
args = args.concat(['-c:a', 'aac', '-c:v', 'libx264']);
|
||||
args = args.concat(['-c:a', 'aac', '-c:v', vcodec]);
|
||||
}
|
||||
args = args.concat(['-preset', 'veryfast', '-tune', 'zerolatency']);
|
||||
if(!config['hwaccel']['type'])
|
||||
args = args.concat(['-preset', 'veryfast']);
|
||||
args = args.concat(['-tune', 'zerolatency']);
|
||||
//if(config['transcode']['format'] === 'dash')
|
||||
args = args.concat(['-remove_at_exit', '1', '-seg_duration', '1', '-window_size', '30']);
|
||||
if(config['transcode']['outputflags'] !== null && config['transcode']['outputflags'] !== "") args = args.concat(config['transcode']['outputflags'].split(" "));
|
||||
|
6
src/v3manual.ts
Normal file
6
src/v3manual.ts
Normal file
@ -0,0 +1,6 @@
|
||||
import * as db from "./database";
|
||||
async function main() {
|
||||
await db.init();
|
||||
await require('./db/3.ts').run();
|
||||
}
|
||||
main();
|
@ -12,6 +12,29 @@
|
||||
xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
|
||||
xhr.send("");
|
||||
}
|
||||
function makeRequest(method, url, payload) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
let xhr = new XMLHttpRequest();
|
||||
xhr.open(method, url);
|
||||
xhr.onload = function () {
|
||||
if (this.status >= 200 && this.status < 300) {
|
||||
resolve(xhr.response);
|
||||
} else {
|
||||
reject({
|
||||
status: this.status,
|
||||
statusText: xhr.statusText
|
||||
});
|
||||
}
|
||||
};
|
||||
xhr.onerror = function () {
|
||||
reject({
|
||||
status: this.status,
|
||||
statusText: xhr.statusText
|
||||
});
|
||||
};
|
||||
!payload ? xhr.send() : xhr.send(payload);
|
||||
});
|
||||
}
|
||||
</script>
|
||||
{% block head %}
|
||||
{% endblock %}
|
||||
@ -29,8 +52,8 @@
|
||||
<div>
|
||||
<div>
|
||||
<b>Satyr</b></br>
|
||||
<a href="https://gitlab.com/knotteye/satyr">About</a></br>
|
||||
<a href="https://gitlab.com/knotteye/satyr/-/releases">v{{ version }}</a>
|
||||
<a href="https://pond.waldn.net/git/knotteye/satyr">About</a></br>
|
||||
<a href="https://pond.waldn.net/git/knotteye/satyr/releases">v{{ version }}</a>
|
||||
</div>
|
||||
<div>
|
||||
<img src="/logo.svg" height="50" />
|
||||
|
@ -1,16 +1,56 @@
|
||||
{% extends "base.njk" %}
|
||||
{% block head %}
|
||||
<!--<script src="/videojs/video.min.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/videojs/video-js.min.css">-->
|
||||
<script src="/dashjs/dash.all.min.js"></script>
|
||||
<script>
|
||||
function startVideo(){
|
||||
var url = "/live/{{username}}/index.mpd";
|
||||
var player = dashjs.MediaPlayer().create();
|
||||
player.initialize(document.querySelector("#videoPlayer"), url, true);
|
||||
console.log('called startvideo');
|
||||
}
|
||||
</script>
|
||||
<script src="/shaka-player.compiled.js"></script>
|
||||
<script>
|
||||
shakaPolyFilled = false;
|
||||
var manifestUri = document.location.protocol+'//'+document.location.host+'/live/{{ username }}/index.mpd';
|
||||
async function initPlayer() {
|
||||
console.log('Trying to initialize player.');
|
||||
if(!shakaPolyFilled){
|
||||
shaka.polyfill.installAll();
|
||||
shakaPolyFilled = true;
|
||||
}
|
||||
var live = JSON.parse(await makeRequest("GET", "/api/{{ username }}/config")).live;
|
||||
if(live){
|
||||
// Create a Player instance.
|
||||
const video = document.getElementById('video');
|
||||
const player = new shaka.Player(video);
|
||||
// Listen for error events.
|
||||
player.addEventListener('error', onErrorEvent);
|
||||
|
||||
video.addEventListener('play', () => {
|
||||
document.getElementById('playbtn').style.visibility = 'hidden';
|
||||
});
|
||||
video.addEventListener('pause', () => {
|
||||
document.getElementById('playbtn').style.visibility = 'visible';
|
||||
});
|
||||
// Try to load a manifest.
|
||||
// This is an asynchronous process.
|
||||
try {
|
||||
await player.load(manifestUri);
|
||||
// This runs if the asynchronous load is successful.
|
||||
console.log('The video has now been loaded!');
|
||||
} catch (e) {
|
||||
// onError is executed if the asynchronous load fails.
|
||||
onError(e);
|
||||
}
|
||||
} else {
|
||||
setTimeout(initPlayer, 5000);
|
||||
}
|
||||
}
|
||||
|
||||
function onErrorEvent(event) {
|
||||
// Extract the shaka.util.Error object from the event.
|
||||
onError(event.detail);
|
||||
}
|
||||
|
||||
function onError(error) {
|
||||
// Log the error.
|
||||
console.error('Error code', error.code, 'object', error);
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', initPlayer);
|
||||
</script>
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
<script>
|
||||
@ -22,13 +62,9 @@ function newPopup(url) {
|
||||
</br>
|
||||
<span style="float: left;font-size: large;"><a href="/live/{{ username }}/index.mpd">{{ username }}</a> | {{ title | escape }}</b></span><span style="float: right;font-size: large;"> Links | <a href="rtmp://{{ domain }}/live/{{ username }}">Watch</a> <a href="JavaScript:newPopup('/chat?room={{ username }}');">Chat</a> <a href="/vods/{{ username }}">VODs</a></span>
|
||||
<div id="jscontainer">
|
||||
<div id="jschild" style="width: 70%;height: 100%;">
|
||||
<!--<video controls poster="/thumbnail.jpg" class="video-js vjs-default-skin" id="live-video" style="width:100%;height:100%;min-height: 500px;"></video>-->
|
||||
<video id="videoPlayer" style="width:100%;height:100%;width: 950px;height: 534px;background-color: rgb(0, 0, 0);" poster="/thumbnail.jpg" autoplay muted></video>
|
||||
|
||||
<!--this spits errors fucking constantly after it tries to reload a video that's already running.. I dunno if it's bad or causing problems so let's just push it to develop and wait for issues!-->
|
||||
<!--it plays the stream without reloading the page tho lol-->
|
||||
<script>window.setInterval(startVideo, 60000)</script>
|
||||
<div id="jschild" style="width: 70%;height: 100%;position: relative;">
|
||||
<image id="playbtn" src="/play.svg" alt="Play Stream" style="width:100%;height:100%;width: 950px;height: 534px;position: absolute;" onclick="document.getElementById('video').paused ? document.getElementById('video').play() : document.getElementById('video') .pause();"></image>
|
||||
<video id="video" style="width:100%;height:100%;width: 950px;height: 534px;background-color: rgb(0, 0, 0);" poster="/thumbnail.jpg" autoplay onclick="this.paused ? this.play() : this.pause();"></video>
|
||||
</div>
|
||||
<div id="jschild" class="webchat" style="width: 30%;height: 100%;position: relative;">
|
||||
<iframe src="/chat?room={{ username }}" frameborder="0" style="width: 100%;height: 100%; min-height: 534px;" allowfullscreen></iframe>
|
||||
@ -37,21 +73,4 @@ function newPopup(url) {
|
||||
</br>
|
||||
<noscript>The webclients for the stream and the chat require javascript, but feel free to use the direct links above!</br></br></noscript>
|
||||
{{ about | escape }}
|
||||
<!--<script>
|
||||
window.HELP_IMPROVE_VIDEOJS = false;
|
||||
var player = videojs('live-video', {
|
||||
html: {
|
||||
nativeCaptions: false,
|
||||
},
|
||||
});
|
||||
player.ready(function() {
|
||||
player.on("error", () => {
|
||||
document.querySelector(".vjs-modal-dialog-content").textContent = "The stream is currently offline.";
|
||||
});
|
||||
player.src({
|
||||
src: '/live/{{ username }}/index.mpd',
|
||||
type: 'application/dash+xml'
|
||||
});
|
||||
})
|
||||
</script>-->
|
||||
{% endblock %}
|
Loading…
x
Reference in New Issue
Block a user