From 91ce108d51b7a71bc5bbf1246addc84b55abf22d Mon Sep 17 00:00:00 2001 From: Braxen Date: Sun, 7 Feb 2021 13:01:57 +0100 Subject: [PATCH 01/11] buttons to run cron manually --- client-vue/package.json | 2 +- client-vue/src/views/About.vue | 18 ++++++++++++++++-- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/client-vue/package.json b/client-vue/package.json index 6fa158f3..1d4a1d17 100644 --- a/client-vue/package.json +++ b/client-vue/package.json @@ -1,6 +1,6 @@ { "name": "twitchautomator-client", - "version": "0.1.3", + "version": "0.1.4", "private": true, "scripts": { "serve": "vue-cli-service serve", diff --git a/client-vue/src/views/About.vue b/client-vue/src/views/About.vue index e9fa5a85..4629cb74 100644 --- a/client-vue/src/views/About.vue +++ b/client-vue/src/views/About.vue @@ -99,9 +99,10 @@

Cronjobs

- - + + +
{{ cron }}
{{ cron_name }} {{ cron_status }}

@@ -180,6 +181,19 @@ export default defineComponent({ }); */ }, + runCron(type: string) { + this.$http + .get(`/api/v0/cron/${type}`) + .then((response) => { + const json = response.data; + console.debug("cronData", json); + if (json.message) alert(json.message); + this.fetchData(); + }) + .catch((err) => { + console.error("about error", err.response); + }); + }, }, }); From 0601fb4dc338d63f73fdf50cfbfae4c8a987b0d9 Mon Sep 17 00:00:00 2001 From: braxen Date: Tue, 9 Feb 2021 11:54:30 +0100 Subject: [PATCH 02/11] start removing old template files --- templates/client.twig | 0 templates/components/logviewer.twig | 29 ---- templates/components/menu.twig | 127 -------------- templates/components/streamer.twig | 49 ------ templates/dialog.twig | 9 - templates/player.twig | 146 ---------------- templates/settings.twig | 249 ---------------------------- templates/tools.twig | 153 ----------------- 8 files changed, 762 deletions(-) delete mode 100644 templates/client.twig delete mode 100644 templates/components/logviewer.twig delete mode 100644 templates/components/menu.twig delete mode 100644 templates/components/streamer.twig delete mode 100644 templates/dialog.twig delete mode 100644 templates/player.twig delete mode 100644 templates/settings.twig delete mode 100644 templates/tools.twig diff --git a/templates/client.twig b/templates/client.twig deleted file mode 100644 index e69de29b..00000000 diff --git a/templates/components/logviewer.twig b/templates/components/logviewer.twig deleted file mode 100644 index 9b1a6f4c..00000000 --- a/templates/components/logviewer.twig +++ /dev/null @@ -1,29 +0,0 @@ -
- {# - {% apply spaceless %} - {% for line in log_lines %} -
- {% if line.source %} - {{ line.date_string }} <{{ line.level }}> [{{ line.source.file|basename }}:{{ line.source.line }} {{line.source.function}}({{ line.source.args ? line.source.args|join : '' }})] {{ line.text }} - {% elseif line.module %} - {{ line.date_string }} <{{ line.module }}> <{{ line.level }}> {{ line.text }} - {% else %} - {{ line.date_string }} <{{ line.level }}> {{ line.text }} - {% endif %} - {% if line.count %}{{ line.count }}{% endif %} - {% if config.debug and line.metadata %}m{% endif %} -
- {% endfor %} - {% endapply %} - #} - - {% for line in log_lines %} - - - - - - - {% endfor %} -
{{ line.date_string }}{{ line.module }}{{ line.level }}{{ line.text }}
-
\ No newline at end of file diff --git a/templates/components/menu.twig b/templates/components/menu.twig deleted file mode 100644 index 5a17605e..00000000 --- a/templates/components/menu.twig +++ /dev/null @@ -1,127 +0,0 @@ -
- - - - - -
- - -
\ No newline at end of file diff --git a/templates/components/streamer.twig b/templates/components/streamer.twig deleted file mode 100644 index 0d0d1368..00000000 --- a/templates/components/streamer.twig +++ /dev/null @@ -1,49 +0,0 @@ -
- -
-
-
-

- - {{ streamer.display_name }} - - {% if streamer.is_live %} - live - {% endif %} -

- - {{ streamer.quality|join(", ") }}{# quality #} - · {{ streamer.vods_list|length }} vods{# vods #} - · {{ streamer.vods_size|formatBytes }}{# total size #} - · - {% if streamer.subbed_at and streamer.expires_at %} - {{ streamer.expires_at|date("Y-m-d") }} - {% else %} - Not subbed - {% endif %} - - {% if streamer.is_live %} - · {# abort recording #} - {% else %} - · {# force recording #} - {% endif %} - {# dump playlist #} - -
-
- - {% if streamer.vods_list|length == 0 %} - -
None
- - {% else %} - - {% for vodclass in streamer.vods_list %} - - {% include 'components/vod.twig' %} - - {% endfor %} - - {% endif %} - -
diff --git a/templates/dialog.twig b/templates/dialog.twig deleted file mode 100644 index 51668eac..00000000 --- a/templates/dialog.twig +++ /dev/null @@ -1,9 +0,0 @@ -{% extends "base.twig" %} - -{% block content %} -
-
Alert
-
{{ text }}
-
-
-{% endblock %} \ No newline at end of file diff --git a/templates/player.twig b/templates/player.twig deleted file mode 100644 index 1a723980..00000000 --- a/templates/player.twig +++ /dev/null @@ -1,146 +0,0 @@ -{% extends "base.twig" %} - -{% block content %} - -
- -
- - -
- - -
-
- -
- - {% for chapter in vodclass.chapters %} - -
-
{{ chapter.title }}
-
{{ chapter.game_name }}
-
- - {% endfor %} - -
- -
-
- - - -
- - -
- -
- - -
- -
- -
- -
- -
- -
- -
- -
- -
- - - -{% endblock %} diff --git a/templates/settings.twig b/templates/settings.twig deleted file mode 100644 index 8c000546..00000000 --- a/templates/settings.twig +++ /dev/null @@ -1,249 +0,0 @@ -{% extends "base.twig" %} - -{% block content %} - -
- -
- -

Streamers

- -
- - {% for streamer in streamers %} - -
-

{{ streamer.username }}

-
-
- - - -
- -
- -

Separate by spaces, e.g. best 1080p 720p audio_only

-
-
- -
- -
- -

Separate by commas, e.g. christmas,media share,opening,po box

-
-
- -
- -
- -
- -
- -
-
- -
-
- -
-
-
- - - (no undo, no confirmation) -
-
-
- - {% endfor %} - -
- -
- -
- -

New streamer

- -
- -
-
- -
- -

Streamer username, case sensitive

-
-
-
- -
-
-
-
- -
-
-
-
- -
-
- -
-
-
- -
-
-
- -
- -
- -
- -

Settings

- -
- -
- - - - {% for index, category in settings %} - {% if category|length > 0 %} -
- {{ index }} - {% for setting in category %} -
- {% if setting.type == "boolean" %} - - {% else %} - -
- {% if setting.type == "string" %} - - {% elseif setting.type == "number" %} - - {% elseif setting.type == "array" %} - - {% endif %} -
- {% endif %} - {% if setting.help %} -

{{ setting.help }}

- {% endif %} -
- {% endfor %} -
- {% endif %} - {% endfor %} - -
- -
- -
- -
- -
- Guessed app url - {{ app_calc }} - - This might not be correct. Please enter it manually, and take proxies and custom port numbers into consideration. -
- -

Check the status of the external utilities on the about page. - - {% if not is_docker %} - -


- - Crontab example
- The Slim framework doesn't have a good way to execute code from the command line, so you'll have to set up cron manually. - {% if config.app_url %} - - 0 5 * * 1 curl {{ config.app_url }}/cron/sub
- 0 */12 * * * curl {{ config.app_url }}/cron/check_muted_vods
- 10 */12 * * * curl {{ config.app_url }}/cron/check_deleted_vods
- 0 1 * * * curl {{ config.app_url }}/cron/playlist_dump -
- This will subscribe to the webhook every 5 days, check muted & deleted vods every 12 hours, and dump playlists once per day. - {% else %} - Can't show example, app url has not been set - {% endif %} - - {% endif %} - -
- -
- -
- -

Favourite games

- -
- -
- -
- {% for id, game in games|sort %} -
- -
- {% endfor %} -
- -
- -
-
- -
- -
- -
- -{% endblock %} diff --git a/templates/tools.twig b/templates/tools.twig deleted file mode 100644 index 525f01c0..00000000 --- a/templates/tools.twig +++ /dev/null @@ -1,153 +0,0 @@ -{% extends "base.twig" %} - -{% block content %} - -
- -
- -

Full VOD fetch and burn chat

- -
- -
- -
- -
- -
-
- -
- -
- -
-
- -
-
- -
-
- -
- -
- -
- -
- -

VOD download

- -
- -
- -
- -
- -
-
- -
- -
- -
-
- -
-
- -
-
- -
- -
- -
- -
- -

Chat download

- -
- -
- -
- -
- -
-
- -
-
- -
-
- -
- -
- -
- -
- -

Saved VODs

- -
- - {% if saved_vods %} -
    - {% for vod in saved_vods %} -
  • {{ vod.name }} ({{ vod.size|formatBytes }})
  • - {% endfor %} -
- {% else %} - None - {% endif %} - -
- -
- -
- -

Current jobs

- -
- - - {% for job in current_jobs %} - - - - - - {% endfor %} -
{{ job.name }}{{ job.pid }}{{ job.status ? 'Running' : 'Unexpected exit' }}{% if job.status %}Kill{% endif %} -
- - {% if not current_jobs %} - None - {% endif %} - -
- -
- -
- -{% endblock %} From 867d6dbc516d4944608852a0c7443757fe081e09 Mon Sep 17 00:00:00 2001 From: braxen Date: Tue, 9 Feb 2021 12:46:59 +0100 Subject: [PATCH 03/11] fix wrong cron playlist dump endpoint in example --- client-vue/package.json | 2 +- client-vue/src/views/Settings.vue | 2 +- package.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/client-vue/package.json b/client-vue/package.json index 1d4a1d17..a8ab5392 100644 --- a/client-vue/package.json +++ b/client-vue/package.json @@ -1,6 +1,6 @@ { "name": "twitchautomator-client", - "version": "0.1.4", + "version": "0.1.5", "private": true, "scripts": { "serve": "vue-cli-service serve", diff --git a/client-vue/src/views/Settings.vue b/client-vue/src/views/Settings.vue index 1863884b..925116fd 100644 --- a/client-vue/src/views/Settings.vue +++ b/client-vue/src/views/Settings.vue @@ -62,7 +62,7 @@ 0 5 * * 1 curl {{ $store.state.config.app_url }}/api/v0/cron/sub
0 */12 * * * curl {{ $store.state.config.app_url }}/api/v0/cron/check_muted_vods
10 */12 * * * curl {{ $store.state.config.app_url }}/api/v0/cron/check_deleted_vods
- 0 1 * * * curl {{ $store.state.config.app_url }}/api/v0/cron/playlist_dump + 0 1 * * * curl {{ $store.state.config.app_url }}/api/v0/cron/dump_playlists This will subscribe to the webhook every 5 days, check muted & deleted vods every 12 hours, and dump playlists once per day. diff --git a/package.json b/package.json index 35a192f8..5b39c1d5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "twitchautomator", - "version": "5.0.3", + "version": "5.0.4", "description": "Twitch VOD automator fluff", "main": "src/Client/main.ts", "scripts": { From e7255b1a0e01cef535afa868b8f2071a21129def Mon Sep 17 00:00:00 2001 From: Braxen Date: Thu, 11 Feb 2021 20:23:41 +0100 Subject: [PATCH 04/11] websocket broker, mainly for docker --- .dockerignore | 3 +- Dockerfile | 1 + client-broker/.dockerignore | 1 + client-broker/Dockerfile | 6 +++ client-broker/package.json | 9 +++++ client-broker/server.js | 58 +++++++++++++++++++++++++++++ client-broker/yarn.lock | 8 ++++ client-vue/src/App.vue | 9 ++--- client-vue/src/twitchautomator.d.ts | 1 + client-vue/src/views/Dashboard.vue | 46 +++++++++++++++++++++++ composer.json | 3 +- composer.lock | 47 ++++++++++++++++++++++- docker-compose.yml | 6 ++- docker/nginx.conf | 15 ++++++++ src/Routes/routes.php | 9 +++++ src/TwitchConfig.php | 1 + src/TwitchHelper.php | 32 +++++----------- 17 files changed, 223 insertions(+), 32 deletions(-) create mode 100644 client-broker/.dockerignore create mode 100644 client-broker/Dockerfile create mode 100644 client-broker/package.json create mode 100644 client-broker/server.js create mode 100644 client-broker/yarn.lock diff --git a/.dockerignore b/.dockerignore index 0918a81f..30461e40 100644 --- a/.dockerignore +++ b/.dockerignore @@ -19,4 +19,5 @@ public/saved_vods client-vue/node_modules client-vue/dist client-vue/.env -client-vue/.env.production \ No newline at end of file +client-vue/.env.production +client-broker \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index b169c900..4f3472d5 100644 --- a/Dockerfile +++ b/Dockerfile @@ -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 diff --git a/client-broker/.dockerignore b/client-broker/.dockerignore new file mode 100644 index 00000000..40b878db --- /dev/null +++ b/client-broker/.dockerignore @@ -0,0 +1 @@ +node_modules/ \ No newline at end of file diff --git a/client-broker/Dockerfile b/client-broker/Dockerfile new file mode 100644 index 00000000..1bffe444 --- /dev/null +++ b/client-broker/Dockerfile @@ -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" ] \ No newline at end of file diff --git a/client-broker/package.json b/client-broker/package.json new file mode 100644 index 00000000..e259e6d8 --- /dev/null +++ b/client-broker/package.json @@ -0,0 +1,9 @@ +{ + "name": "client-broker", + "version": "1.0.0", + "main": "server.js", + "license": "MIT", + "dependencies": { + "ws": "^7.4.3" + } +} diff --git a/client-broker/server.js b/client-broker/server.js new file mode 100644 index 00000000..196c9b85 --- /dev/null +++ b/client-broker/server.js @@ -0,0 +1,58 @@ +// const { cli } = require('webpack'); +const WebSocket = require('ws'); + +class ClientBroker { + constructor(){ + this.clients = []; + this.wss = null; + } + start () { + console.log('Starting...'); + this.wss = new WebSocket.Server({ port: 8765 }); + + 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); + 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", data); + console.debug(`Clients: ${this.wss.clients.size}`); + } +} + +const cb = new ClientBroker(); +cb.start(); \ No newline at end of file diff --git a/client-broker/yarn.lock b/client-broker/yarn.lock new file mode 100644 index 00000000..04e8fb52 --- /dev/null +++ b/client-broker/yarn.lock @@ -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== diff --git a/client-vue/src/App.vue b/client-vue/src/App.vue index c09584df..73facbed 100644 --- a/client-vue/src/App.vue +++ b/client-vue/src/App.vue @@ -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) @@ -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: { diff --git a/client-vue/src/twitchautomator.d.ts b/client-vue/src/twitchautomator.d.ts index 3abb5777..af7d6923 100644 --- a/client-vue/src/twitchautomator.d.ts +++ b/client-vue/src/twitchautomator.d.ts @@ -145,6 +145,7 @@ export type ApiConfig = { process_wait_method: number; youtube_api_key: string; favourites: Record; + websocket_enabled: boolean; }; export type ApiStreamer = { diff --git a/client-vue/src/views/Dashboard.vue b/client-vue/src/views/Dashboard.vue index 125869da..129aba42 100644 --- a/client-vue/src/views/Dashboard.vue +++ b/client-vue/src/views/Dashboard.vue @@ -94,6 +94,7 @@ export default defineComponent({ logModule: "", oldData: {} as Record, notificationSub: Function as any, + ws: {} as WebSocket, }; }, created() { @@ -116,6 +117,12 @@ export default defineComponent({ }, 1000); this.processNotifications(); + + if (this.$store.state.config.websocket_enabled) { + this.connectWebsocket(); + } else { + console.debug("No websocket url"); + } }, unmounted() { if (this.interval) { @@ -129,6 +136,45 @@ export default defineComponent({ } }, methods: { + connectWebsocket() { + const websocket_url = + process.env.NODE_ENV === "development" ? "ws://localhost:8765/socket/" : this.$store.state.config.app_url.replace(/https?/, "ws") + "/socket/"; + console.log(`Connecting to ${websocket_url}`); + this.ws = new WebSocket(websocket_url); + this.ws.onopen = (ev: Event) => { + console.log("ws open", ev); + this.ws.send(JSON.stringify({ action: "helloworld" })); + }; + this.ws.onmessage = (ev: MessageEvent) => { + // console.log("ws message", ev); + let text = ev.data; + let json: any = {}; + try { + json = JSON.parse(text); + } catch (error) { + console.error("Couldn't parse json", text); + return; + } + // console.log("json return", json); + // this.$emit("websocketData", json); + if (json.data.action && ["start_capture", "finish_capture", "chapter_update"].indexOf(json.data.action) !== -1) { + console.log("Websocket update"); + this.fetchStreamers().then((sl) => { + this.$store.commit("updateStreamerList", sl); + this.loading = false; + }); + } else { + console.log(`Websocket wrong action (${json.data.action})`); + } + }; + this.ws.onerror = (ev: Event) => { + console.log("ws error", ev); + }; + this.ws.onclose = (ev: CloseEvent) => { + console.log("ws close", ev); + }; + return this.ws; + }, async fetchStreamers() { let response; try { diff --git a/composer.json b/composer.json index 90dbdd19..b9c6b6a3 100644 --- a/composer.json +++ b/composer.json @@ -12,7 +12,8 @@ "guzzlehttp/guzzle": "7.0", "tuupola/slim-basic-auth": "^3.2", "symfony/process": "^5.1", - "twig/html-extra": "^3.0" + "twig/html-extra": "^3.0", + "textalk/websocket": "^1.5" }, "autoload": { "psr-4": { diff --git a/composer.lock b/composer.lock index 73676117..2ed8fb4b 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "9af8117743cb429c1cd894bb4c6bace3", + "content-hash": "34d1acca135f1f0581def72b01cfa881", "packages": [ { "name": "fig/http-message-util", @@ -1933,6 +1933,51 @@ ], "time": "2020-12-08T17:03:37+00:00" }, + { + "name": "textalk/websocket", + "version": "1.5.1", + "source": { + "type": "git", + "url": "https://github.com/Textalk/websocket-php.git", + "reference": "aada79791f013d821c8265e9e2ae565aa9bdca2f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Textalk/websocket-php/zipball/aada79791f013d821c8265e9e2ae565aa9bdca2f", + "reference": "aada79791f013d821c8265e9e2ae565aa9bdca2f", + "shasum": "" + }, + "require": { + "php": "^7.2 | ^8.0", + "psr/log": "^1.0" + }, + "require-dev": { + "php-coveralls/php-coveralls": "^2.0", + "phpunit/phpunit": "^8.0|^9.0", + "squizlabs/php_codesniffer": "^3.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "WebSocket\\": "lib" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "ISC" + ], + "authors": [ + { + "name": "Fredrik Liljegren" + }, + { + "name": "Sören Jensen", + "email": "soren@abicart.se" + } + ], + "description": "WebSocket client and server", + "time": "2021-02-07T19:10:14+00:00" + }, { "name": "tuupola/callable-handler", "version": "1.1.0", diff --git a/docker-compose.yml b/docker-compose.yml index 92afcf91..b0c56c9a 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -4,7 +4,7 @@ services: build: . image: mrbrax/twitchautomator ports: - - "8081:8080" + - "8082:8080" volumes: - ./config:/var/www/twitchautomator/config - ./cache:/var/www/twitchautomator/cache @@ -13,4 +13,6 @@ services: - ./docker/saved_clips:/var/www/twitchautomator/public/saved_clips - ./docker/saved_vods:/var/www/twitchautomator/public/saved_vods cron: - build: ./docker/cron-container/ \ No newline at end of file + build: ./docker/cron-container/ + broker: + build: ./client-broker/ \ No newline at end of file diff --git a/docker/nginx.conf b/docker/nginx.conf index fe101e3a..f79a2a3f 100644 --- a/docker/nginx.conf +++ b/docker/nginx.conf @@ -81,6 +81,21 @@ http { include fastcgi_params; fastcgi_pass 127.0.0.1:9000; } + + location ~ /socket/ { + proxy_pass http://broker:8765; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "Upgrade"; + proxy_set_header Host $host; + } + + # upstream ws-backend { + # # enable sticky session based on IP + # ip_hash; + # server broker:8765; + # } + } gzip on; diff --git a/src/Routes/routes.php b/src/Routes/routes.php index 87dc8eb2..bc88a72d 100644 --- a/src/Routes/routes.php +++ b/src/Routes/routes.php @@ -12,6 +12,7 @@ use App\Controller\CronController; use App\TwitchConfig; +use App\TwitchHelper; /** @var \Slim\App $app */ @@ -163,6 +164,14 @@ $group->get('/log/{filename}[/{last_line}]', ApiController::class . ':display_log')->setName('api_display_log'); + $group->any('/test_webhook', function (Request $request, Response $response, array $args) { + TwitchHelper::webhook([ + 'action' => 'test' + ]); + $response->getBody()->write("Tested"); + return $response; + }); + // $group->post('/hook', ApiController::class . ':hook')->setName('hook_post'); // $group->get('/playlist_dump/{username}', ApiController::class . ':playlist_dump')->setName('api_playlist_dump'); diff --git a/src/TwitchConfig.php b/src/TwitchConfig.php index 689c9007..1dfc46a2 100644 --- a/src/TwitchConfig.php +++ b/src/TwitchConfig.php @@ -26,6 +26,7 @@ class TwitchConfig ['key' => 'webhook_url', 'group' => 'Basic', 'text' => 'Webhook URL', 'type' => 'string', 'help' => 'For external scripting'], ['key' => 'password', 'group' => 'Interface', 'text' => 'Password', 'type' => 'string', 'help' => 'Keep blank for none. Username is admin'], ['key' => 'password_secure', 'group' => 'Interface', 'text' => 'Force HTTPS for password', 'type' => 'boolean', 'default' => true], + ['key' => 'websocket_enabled', 'group' => 'Interface', 'text' => 'Websockets enabled', 'type' => 'boolean'], ['key' => 'storage_per_streamer', 'group' => 'Basic', 'text' => 'Gigabytes of storage per streamer', 'type' => 'number', 'default' => 100], ['key' => 'hls_timeout', 'group' => 'Advanced', 'text' => 'HLS Timeout in seconds (ads)', 'type' => 'number', 'default' => 200], ['key' => 'vods_to_keep', 'group' => 'Basic', 'text' => 'VODs to keep per streamer', 'type' => 'number', 'default' => 5], diff --git a/src/TwitchHelper.php b/src/TwitchHelper.php index e74dbd6a..f0c24bef 100644 --- a/src/TwitchHelper.php +++ b/src/TwitchHelper.php @@ -7,6 +7,7 @@ use GuzzleHttp\Client; use Symfony\Component\Process\Process; use App\TwitchAutomatorJob; +use WebSocket; class TwitchHelper { @@ -1105,29 +1106,17 @@ public static function findJob(string $search) public static function webhook(array $data) { - if (!TwitchConfig::cfg('webhook_url')) { - return; - // throw new \Exception("No webhook URL set"); + if(TwitchConfig::cfg('websocket_enabled') || getenv('TCD_DOCKER') == 1 ){ + $websocket_url = getenv('TCD_DOCKER') == 1 ? "ws://broker:8765/socket/" : preg_replace("https?", "ws", TwitchConfig::cfg('app_url')) . "/socket/"; + $client = new Websocket\Client($websocket_url); + $client->text(json_encode([ + 'server' => true, + 'data' => $data + ])); + $client->close(); } - $client = new \GuzzleHttp\Client(); - - try { - $client->request("POST", TwitchConfig::cfg('webhook_url'), [ - 'form_params' => $data, - 'connect_timeout' => 5, - 'timeout' => 10 - ]); - } catch (\Throwable $th) { - self::logAdvanced(self::LOG_ERROR, "helper", "Webhook POST error: " . $th->getMessage()); - } - } - - /* - public static function websocket(array $data) - { - - if (!TwitchConfig::cfg('websocket_url')) { + if (!TwitchConfig::cfg('webhook_url')) { return; // throw new \Exception("No webhook URL set"); } @@ -1144,7 +1133,6 @@ public static function websocket(array $data) self::logAdvanced(self::LOG_ERROR, "helper", "Webhook POST error: " . $th->getMessage()); } } - */ public static function vodFolder(string $username = null) { From 05eb1dd775f6d7a1286a5cf0355044177e9baa5f Mon Sep 17 00:00:00 2001 From: Braxen Date: Thu, 11 Feb 2021 20:35:40 +0100 Subject: [PATCH 05/11] custom ports, detect https --- client-broker/server.js | 6 ++++-- client-vue/src/views/Dashboard.vue | 5 +++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/client-broker/server.js b/client-broker/server.js index 196c9b85..67a5577d 100644 --- a/client-broker/server.js +++ b/client-broker/server.js @@ -7,8 +7,10 @@ class ClientBroker { this.wss = null; } start () { - console.log('Starting...'); - this.wss = new WebSocket.Server({ port: 8765 }); + const serverPort = process.argv[2] ? process.argv[2] : 8765; + + console.log(`Starting on port ${serverPort}...`); + this.wss = new WebSocket.Server({ port: serverPort }); this.wss.on('connection', this.onConnect.bind(this)); } diff --git a/client-vue/src/views/Dashboard.vue b/client-vue/src/views/Dashboard.vue index 129aba42..0598355a 100644 --- a/client-vue/src/views/Dashboard.vue +++ b/client-vue/src/views/Dashboard.vue @@ -137,8 +137,9 @@ export default defineComponent({ }, methods: { connectWebsocket() { - const websocket_url = - process.env.NODE_ENV === "development" ? "ws://localhost:8765/socket/" : this.$store.state.config.app_url.replace(/https?/, "ws") + "/socket/"; + const proto = window.location.protocol === "https:" ? "wss://" : "ws://"; + const websocket_url_public = proto + window.location.host + this.$store.state.config.basepath + "/socket/"; + const websocket_url = process.env.NODE_ENV === "development" ? "ws://localhost:8765/socket/" : websocket_url_public; console.log(`Connecting to ${websocket_url}`); this.ws = new WebSocket(websocket_url); this.ws.onopen = (ev: Event) => { From 3aebeb487767e6b9bb07ccaed6b0160b3bf9b945 Mon Sep 17 00:00:00 2001 From: Braxen Date: Thu, 11 Feb 2021 22:45:36 +0100 Subject: [PATCH 06/11] misc --- client-vue/package.json | 2 +- .../src/components/SideMenuStreamer.vue | 2 +- client-vue/src/components/Vod.vue | 2 +- client-vue/src/views/Dashboard.vue | 17 ++++- composer.json | 1 - composer.lock | 69 +------------------ package.json | 2 +- src/TwitchHelper.php | 2 +- 8 files changed, 22 insertions(+), 75 deletions(-) diff --git a/client-vue/package.json b/client-vue/package.json index a8ab5392..4e16a017 100644 --- a/client-vue/package.json +++ b/client-vue/package.json @@ -1,6 +1,6 @@ { "name": "twitchautomator-client", - "version": "0.1.5", + "version": "0.2.0", "private": true, "scripts": { "serve": "vue-cli-service serve", diff --git a/client-vue/src/components/SideMenuStreamer.vue b/client-vue/src/components/SideMenuStreamer.vue index 943198b8..b9522673 100644 --- a/client-vue/src/components/SideMenuStreamer.vue +++ b/client-vue/src/components/SideMenuStreamer.vue @@ -28,7 +28,7 @@ {{ streamer.current_game.game_name }} for Current duration:
  • Watch live: - Twitch + Twitch
  • - - - - - + + + + + + + + + + + + + + - {{ formatDate(vod.dt_started_at.date) }} - {{ humanDate(vod.dt_started_at.date, true) }} + + + {{ formatDate(vod.dt_started_at.date) }} + + + {{ humanDate(vod.dt_started_at.date, true) }} diff --git a/client-vue/src/components/Vod.vue b/client-vue/src/components/Vod.vue index 27192850..a1dcc3b0 100644 --- a/client-vue/src/components/Vod.vue +++ b/client-vue/src/components/Vod.vue @@ -173,7 +173,8 @@
  • - The ID was {{ vod?.twitch_vod_id }}. + The ID was {{ vod?.twitch_vod_id }}. The VOD probably never got saved.
  • @@ -348,7 +349,8 @@