Skip to content

Commit

Permalink
Merge pull request #34 from MrBrax/develop
Browse files Browse the repository at this point in the history
Develop
  • Loading branch information
MrBrax authored Feb 12, 2021
2 parents 4c8b429 + 8474227 commit 1136b43
Show file tree
Hide file tree
Showing 45 changed files with 789 additions and 960 deletions.
3 changes: 2 additions & 1 deletion .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,5 @@ public/saved_vods
client-vue/node_modules
client-vue/dist
client-vue/.env
client-vue/.env.production
client-vue/.env.production
client-broker
1 change: 1 addition & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ ENV TCD_BIN_DIR=/usr/bin
ENV TCD_FFMPEG_PATH=/usr/bin/ffmpeg
ENV TCD_MEDIAINFO_PATH=/usr/bin/mediainfo
ENV TCD_DOCKER=1
ENV TCD_WEBSOCKET_ENABLED=1

USER nobody
WORKDIR /var/www/twitchautomator
16 changes: 10 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,16 +26,20 @@ Reminder that i don't use docker myself on my capturing setup, so any specific e
Known issues:
- TwitchDownloaderCLI doesn't work in alpine/docker: /bin/sh: ./TwitchDownloaderCLI: not found

### Manual build (recommended)
Run `docker-compose up --build -d` in the app directory. The docker-compose.yml file is required.
### Docker hub

If you want the public webapp to have a custom base folder, you must provide `BASE_URL` and `VUE_APP_BASE_URL` in the environment variable settings.
1. Download the [docker-compose.yml](https://raw.githubusercontent.com/MrBrax/TwitchAutomator/master/docker-compose.yml) file and place it in a directory.
2. Run `docker-compose pull` and `docker-compose up -d` to start it.
3. Visit the webapp at `localhost:8082`

### Docker hub
Hub: https://hub.docker.com/r/mrbrax/twitchautomator

Pull the image from https://hub.docker.com/r/mrbrax/twitchautomator
*The dockerhub build is preconfigured to be hosted at the root (`/`) and such, does not work when placed in a subdirectory.*

Docker hub doesn't seem to fully support docker-compose apps, so the cron stuff won't work.
### Manual build
Run `docker-compose up --build -d` in the app directory. The `docker-compose.yml` file is required.

If you want the public webapp to have a custom base folder, you must provide `BASE_URL` and `VUE_APP_BASE_URL` in the environment variable settings.

## Standalone setup

Expand Down
1 change: 1 addition & 0 deletions client-broker/.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
node_modules/
6 changes: 6 additions & 0 deletions client-broker/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
FROM alpine:3.7
RUN apk --no-cache add nodejs yarn
COPY . /var/broker
WORKDIR /var/broker
RUN yarn install
ENTRYPOINT [ "yarn", "start" ]
9 changes: 9 additions & 0 deletions client-broker/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"name": "client-broker",
"version": "1.0.0",
"main": "server.js",
"license": "MIT",
"dependencies": {
"ws": "^7.4.3"
}
}
77 changes: 77 additions & 0 deletions client-broker/server.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
// const { cli } = require('webpack');
const WebSocket = require('ws');

class ClientBroker {
constructor(){
this.clients = [];
this.wss = null;
}
start () {
const a = process.argv.slice(2);
const serverPort = a[0] ? parseInt(a[0]) : 8765;

console.log(`Starting on port ${serverPort}...`);
try {
this.wss = new WebSocket.Server({ port: serverPort });
} catch (error) {
console.error("Fatal error when starting broker server", error);
return false;
}

this.wss.on('error', (error) => {
console.log("Websocket server error", error);
});

this.wss.on('connection', this.onConnect.bind(this));
}

onConnect(ws, req){
const clientIP = req.connection.remoteAddress;
ws.clientIP = clientIP;
// console.log(clientIP);
this.clients.push(ws);

ws.on("message", (message) => this.onMessage(ws, message));
ws.on("pong", (heartbeat) => {
ws.isAlive = true;
console.log(`Pong from ${clientIP}`);
});
ws.on("error", (err) => {
console.error("Client error", err)
});
}

onMessage(ws, message){
// console.log("message", ws, message);

if(message == "ping"){
console.log(`Pong to ${ws.clientIP}`);
ws.send("pong");
return;
}

let data;

try {
data = JSON.parse(message);
} catch (error) {
console.error(`Invalid data from ${ws.clientIP}: ${message}`)
return;
}

if(data.server){
this.wss.clients.forEach((client) => {
client.send(JSON.stringify({
action: "server",
data: data.data
}));
});
}

console.log(`json from ${ws.clientIP}:`, data);
console.debug(`Clients: ${this.wss.clients.size}`);
}
}

const cb = new ClientBroker();
cb.start();
8 changes: 8 additions & 0 deletions client-broker/yarn.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1


ws@^7.4.3:
version "7.4.3"
resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.3.tgz#1f9643de34a543b8edb124bdcbc457ae55a6e5cd"
integrity sha512-hr6vCR76GsossIRsr8OLR9acVVm1jyfEWvhbNjtgPOrfvAlKzvyeg/P6r8RuDjRyrcQoPQT7K0DGEPc7Ae6jzA==
4 changes: 3 additions & 1 deletion client-vue/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "twitchautomator-client",
"version": "0.1.3",
"version": "0.2.0",
"private": true,
"scripts": {
"serve": "vue-cli-service serve",
Expand All @@ -19,6 +19,7 @@
"core-js": "^3.6.5",
"date-fns": "^2.16.1",
"normalize.css": "^8.0.1",
"register-service-worker": "^1.7.1",
"vue": "3.0.5",
"vue-axios": "^3.2.2",
"vue-router": "4.0.3",
Expand All @@ -29,6 +30,7 @@
"@typescript-eslint/parser": "^4.14.0",
"@vue/cli-plugin-babel": "~4.5.0",
"@vue/cli-plugin-eslint": "~4.5.0",
"@vue/cli-plugin-pwa": "~4.5.0",
"@vue/cli-plugin-router": "~4.5.0",
"@vue/cli-plugin-typescript": "~4.5.0",
"@vue/cli-plugin-vuex": "~4.5.0",
Expand Down
2 changes: 2 additions & 0 deletions client-vue/public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<!--
<link rel="apple-touch-icon" sizes="180x180" href="<%= BASE_URL %>manifest/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="<%= BASE_URL %>manifest/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="<%= BASE_URL %>manifest/favicon-16x16.png">
Expand All @@ -14,6 +15,7 @@
<meta name="msapplication-TileColor" content="#000000">
<meta name="msapplication-config" content="/manifest/browserconfig.xml">
<meta name="theme-color" content="#ffffff">
-->
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,18 @@
"short_name": "TwitchAutomator",
"icons": [
{
"src": "android-chrome-192x192.png",
"src": "manifest/android-chrome-192x192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "android-chrome-512x512.png",
"src": "manifest/android-chrome-512x512.png",
"sizes": "512x512",
"type": "image/png"
}
],
"theme_color": "#0072bc",
"background_color": "#ffffff",
"display": "standalone",
"start_url": "../dashboard"
"start_url": "."
}
2 changes: 2 additions & 0 deletions client-vue/public/robots.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
User-agent: *
Disallow:
9 changes: 4 additions & 5 deletions client-vue/src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export default defineComponent({
this.fetchData();
},
methods: {
fetchData() {
async fetchData() {
// client config
const currentClientConfig = localStorage.getItem("twitchautomator_config")
? JSON.parse(localStorage.getItem("twitchautomator_config") as string)
Expand All @@ -47,10 +47,9 @@ export default defineComponent({
// clear config
this.$store.commit("updateConfig", []);
return this.$http.get(`/api/v0/settings/list`).then((response) => {
this.$store.commit("updateConfig", response.data.data.config);
this.$store.commit("updateVersion", response.data.data.version);
});
const response = await this.$http.get(`/api/v0/settings/list`);
this.$store.commit("updateConfig", response.data.data.config);
this.$store.commit("updateVersion", response.data.data.version);
},
},
components: {
Expand Down
4 changes: 4 additions & 0 deletions client-vue/src/assets/_mobile.scss
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,10 @@
display: none;
}

#jobs-status {
display: none;
}

.jobs_list {
display: none;
}
Expand Down
3 changes: 2 additions & 1 deletion client-vue/src/components/DurationDisplay.vue
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ export default defineComponent({
if ((dur.seconds && dur.seconds > 0) || (dur.minutes && dur.minutes > 0)) str += `${dur.seconds}s `;
this.timeString = str.trim();
} else {
this.timeString = dur.hours?.toString().padStart(2, "0") + ":" + dur.minutes?.toString().padStart(2, "0") + ":" + dur.seconds?.toString().padStart(2, "0");
this.timeString =
dur.hours?.toString().padStart(2, "0") + ":" + dur.minutes?.toString().padStart(2, "0") + ":" + dur.seconds?.toString().padStart(2, "0");
}
},
},
Expand Down
62 changes: 45 additions & 17 deletions client-vue/src/components/SideMenuStreamer.vue
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
<strong>{{ streamer.current_game.game_name }}</strong>
</template>
<template v-else>
Playing <strong>{{ streamer.current_game.game_name }}</strong>
Playing <strong>{{ streamer.current_game ? streamer.current_game.game_name : "(none)" }}</strong>
</template>
for
<duration-display
Expand Down Expand Up @@ -62,36 +62,64 @@
}"
:title="formatDate(vod.dt_started_at.date)"
>
<!-- main icon -->
<span class="icon" v-if="vod.is_capturing"><fa icon="sync" spin></fa></span><!-- capturing -->
<span class="icon" v-else-if="vod.is_converting"><fa icon="cog" spin></fa></span><!-- converting -->
<span class="icon" v-else-if="vod.api_hasFavouriteGame"><fa icon="star"></fa></span><!-- favourite -->
<span class="icon" v-else-if="!vod.is_capturing && !vod.is_converting && !vod.is_finalized"><fa :icon="['far', 'hourglass']"></fa></span> <!-- waiting after capture -->
<span class="icon" v-else-if="vod.is_finalized"><fa icon="film"></fa></span><!-- video -->
<!-- capturing -->
<span class="icon" v-if="vod.is_capturing"><fa icon="sync" spin></fa></span>

<!-- converting -->
<span class="icon" v-else-if="vod.is_converting"><fa icon="cog" spin></fa></span>

<!-- favourite -->
<span class="icon" v-else-if="vod.api_hasFavouriteGame"><fa icon="star"></fa></span>

<!-- waiting after capture -->
<span class="icon" v-else-if="!vod.is_capturing && !vod.is_converting && !vod.is_finalized"><fa :icon="['far', 'hourglass']"></fa></span>

<!-- video -->
<span class="icon" v-else-if="vod.is_finalized"><fa icon="film"></fa></span>

<!-- started at -->
<span v-if="!$store.state.clientConfig.useRelativeTime && vod.dt_started_at">{{ formatDate(vod.dt_started_at.date) }}</span><!-- absolute time -->
<span v-if="$store.state.clientConfig.useRelativeTime && vod.dt_started_at">{{ humanDate(vod.dt_started_at.date, true) }}</span><!-- relative time -->

<!-- absolute time -->
<span v-if="!$store.state.clientConfig.useRelativeTime && vod.dt_started_at">{{ formatDate(vod.dt_started_at.date) }}</span>

<!-- relative time -->
<span v-if="$store.state.clientConfig.useRelativeTime && vod.dt_started_at">{{ humanDate(vod.dt_started_at.date, true) }}</span>

<!-- when capturing -->
<template v-if="vod.is_capturing">
<span> &middot; (<duration-display :startDate="streamer.current_vod.dt_started_at.date" :outputStyle="$store.state.clientConfig.useRelativeTime ? 'human' : 'numbers'"></duration-display>)</span><!-- duration -->
<span v-if="vod.api_getRecordingSize"> &middot; {{ formatBytes(vod.api_getRecordingSize, 2) }}+</span><!-- filesize -->
<span>
&middot; (<duration-display
:startDate="streamer.current_vod.dt_started_at.date"
:outputStyle="$store.state.clientConfig.useRelativeTime ? 'human' : 'numbers'"
></duration-display
>)</span
><!-- duration -->
<span v-if="vod.api_getRecordingSize"> &middot; {{ formatBytes(vod.api_getRecordingSize, 2) }}+</span
><!-- filesize -->
</template>

<!-- when not capturing -->
<template v-else>
<span v-if="vod.duration_seconds"> &middot; ({{ $store.state.clientConfig.useRelativeTime ? niceDuration(vod.duration_seconds) : humanDuration(vod.duration_seconds) }})</span><!-- duration -->
<span v-if="vod.total_size"> &middot; {{ formatBytes(vod.total_size, 2) }}</span><!-- filesize -->
<span v-if="vod.duration_seconds">
&middot; ({{
$store.state.clientConfig.useRelativeTime ? niceDuration(vod.duration_seconds) : humanDuration(vod.duration_seconds)
}})</span
><!-- duration -->
<span v-if="vod.total_size"> &middot; {{ formatBytes(vod.total_size, 2) }}</span
><!-- filesize -->
</template>

<!-- flags -->
<template v-if="vod.is_finalized">
<span class="flags">
<span v-if="vod.twitch_vod_exists === false" class="icon is-error" title="Deleted"><fa icon="trash"></fa></span><!-- vod deleted -->
<span v-if="vod.twitch_vod_exists === null" class="icon is-error" title="Not checked"><fa icon="question"></fa></span><!-- vod not checked -->
<span v-if="vod.twitch_vod_muted === true" class="icon is-error" title="Muted"><fa icon="volume-mute"></fa></span><!-- vod muted -->
<span v-if="vod.is_capture_paused" class="icon is-error" title="Paused"><fa icon="pause"></fa></span><!-- capturing paused -->
<span v-if="vod.twitch_vod_exists === false" class="icon is-error" title="Deleted"><fa icon="trash"></fa></span
><!-- vod deleted -->
<span v-if="vod.twitch_vod_exists === null" class="icon is-error" title="Not checked"><fa icon="question"></fa></span
><!-- vod not checked -->
<span v-if="vod.twitch_vod_muted === true" class="icon is-error" title="Muted"><fa icon="volume-mute"></fa></span
><!-- vod muted -->
<span v-if="vod.is_capture_paused" class="icon is-error" title="Paused"><fa icon="pause"></fa></span
><!-- capturing paused -->
</span>
</template>

Expand Down
8 changes: 5 additions & 3 deletions client-vue/src/components/Vod.vue
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,8 @@
</li>
<li>
<span v-if="vod?.twitch_vod_id">
The ID was <a :href="vod?.twitch_vod_url" rel="noreferrer" target="_blank">{{ vod?.twitch_vod_id }}</a>.
The ID was <a :href="vod?.twitch_vod_url" rel="noreferrer" target="_blank">{{ vod?.twitch_vod_id }}</a
>.
</span>
<span v-else>The VOD probably never got saved.</span>
</li>
Expand All @@ -195,7 +196,7 @@
<li><strong>Current duration:</strong><duration-display :startDate="vod.dt_started_at.date" outputStyle="human"></duration-display></li>
<li>
<strong>Watch live:</strong>
<a href="https://twitch.tv/{{ streamer.display_name }}" rel="noreferrer" target="_blank">Twitch</a>
<a :href="'https://twitch.tv/' + vod.streamer_name" rel="noreferrer" target="_blank">Twitch</a>
</li>
<!--<li><strong>Watch capture:</strong>
<a href="{{ base_path() }}/vods/{{ config.channel_folders ? vodclass.streamer_name ~ "/" : "" }}{{ vodclass.basename }}.ts" rel="noreferrer" target="_blank">TS file</a>
Expand Down Expand Up @@ -348,7 +349,8 @@
<template v-else-if="vod?.is_capturing">
<em class="text-overflow">
<span class="icon"><fa icon="video"></fa></span>
Capturing to <strong>{{ vod?.basename }}.ts</strong> (<strong>{{ formatBytes(vod?.api_getRecordingSize) }}</strong>)
Capturing to <strong>{{ vod?.basename }}.ts</strong> (<strong>{{ formatBytes(vod?.api_getRecordingSize) }}</strong
>)
</em>

<br />
Expand Down
3 changes: 2 additions & 1 deletion client-vue/src/components/forms/SettingsForm.vue
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,8 @@
v-for="item in data.choices"
:key="item"
:selected="
(settingsData[data.key] !== undefined && settingsData[data.key] === item) || (settingsData[data.key] === undefined && item === data.default)
(settingsData[data.key] !== undefined && settingsData[data.key] === item) ||
(settingsData[data.key] === undefined && item === data.default)
"
>
{{ item }}
Expand Down
Loading

0 comments on commit 1136b43

Please sign in to comment.