367 Commits

Author SHA1 Message Date
Owncast
c49fdb09e5 Bundle embedded web app 2025-02-16 01:07:49 +00:00
nya/nekojanai
e08b251b7c fix: username wrapping for parting message (#4209) 2025-02-15 17:02:56 -08:00
Owncast
eaf2e4b12a Bundle embedded web app 2025-02-15 06:54:50 +00:00
dependabot[bot]
81dad75986 chore(deps): bump dompurify from 3.2.3 to 3.2.4 in /web (#4210)
Bumps [dompurify](https://github.com/cure53/DOMPurify) from 3.2.3 to 3.2.4.
- [Release notes](https://github.com/cure53/DOMPurify/releases)
- [Commits](https://github.com/cure53/DOMPurify/compare/3.2.3...3.2.4)

---
updated-dependencies:
- dependency-name: dompurify
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-02-14 22:50:02 -08:00
mahmed2000
849c4e85b0 Removes trailing whitespace causing web hydration errors (#4208) 2025-02-14 22:49:08 -08:00
Owncast
a29385e31c Bundle embedded web app 2025-02-15 06:37:06 +00:00
Yash Kumar
2c42b901f2 set default initialValue to an empty string in TextFieldWithSubmit component (#4207) 2025-02-14 22:32:24 -08:00
Owncast
a963ac0135 Bundle embedded web app 2025-02-15 06:26:42 +00:00
Gabe Kangas
0facdd5330 fix(deps): fix emojimart version again 2025-02-14 22:21:09 -08:00
renovate[bot]
e9a78bd1d0 chore(deps): update alpine docker tag to v3.21.3 2025-02-14 19:42:56 +00:00
Gabe Kangas
e50e72af5b fix(ap): resolve issue where follows would not work if private mode was enabled. Fixes #4142 (#4202) 2025-02-12 23:43:09 -08:00
Gabe Kangas
4b627f0693 chore(go): migrate more models to codegen versions. For #3778 2025-02-12 21:18:47 -08:00
Owncast
8bdb52fd37 Bundle embedded web app 2025-02-13 04:51:44 +00:00
Germaine Lee
f1ca5f9549 Update follower cards to have better responsive design (#4198)
* change a follower's name to be very long

* Add more media query and make the entire row match the tallest card

* fix lint in followerCollection file

* make media queries easier to read
2025-02-12 20:46:54 -08:00
Owncast
788b582e35 Bundle embedded web app 2025-02-13 03:25:07 +00:00
Gabe Kangas
ca98a5ad21 fix(video): support native hls playback via opengraph tags. Fixes #4204 2025-02-12 19:19:42 -08:00
Owncast
3401dacdbc Bundle embedded web app 2025-02-12 20:00:14 +00:00
github-actions[bot]
8a9967435c Updated translations (#4203)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2025-02-12 11:55:19 -08:00
renovate[bot]
6d6edda5e3 chore(deps): update peter-evans/create-or-update-comment digest to 54ad810 2025-02-12 05:51:16 +00:00
Owncast
a611fd93ba Commit updated API documentation 2025-02-12 05:50:24 +00:00
Gabe Kangas
d4dec25129 chore(api): add integration version of the status api. Closes #3981 2025-02-11 21:48:33 -08:00
Gabe Kangas
7b88b1099d Revert "fixed write header bug in case of errors in index html (#4185)"
This reverts commit 8af820e60e.
2025-02-11 18:37:13 -08:00
Owncast
10e5683bd3 Bundle embedded web app 2025-02-12 01:08:52 +00:00
github-actions[bot]
62c938e7db Updated translations (#4200)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2025-02-11 17:04:06 -08:00
Owncast
048632dee2 Bundle embedded web app 2025-02-11 23:45:12 +00:00
Srilekha
ed5186a280 Added changes for Issue 4044 (#4199)
* Added changes for fix 4044

* Fixed spacing issue

---------

Co-authored-by: Gabe Kangas <gabek@real-ity.com>
2025-02-11 15:40:11 -08:00
renovate[bot]
2c002d8b54 fix(deps): update module golang.org/x/net to v0.35.0 2025-02-10 17:40:45 +00:00
renovate[bot]
d6fc08fb03 fix(deps): update module golang.org/x/crypto to v0.33.0 2025-02-08 03:03:20 +00:00
Owncast
492ceeb286 Bundle embedded web app 2025-02-08 03:01:58 +00:00
Germaine Lee
9be8fa56c2 Add background color and title attribute to social images (#4192)
* Make social links wrap

* Add background to social links

* add title to social link

* fix lint attempt

* css lint fix attempt

* scss prettier

---------

Co-authored-by: Gabe Kangas <gabek@real-ity.com>
2025-02-07 18:56:24 -08:00
dependabot[bot]
7eb415112b chore(deps): bump jsonpath-plus from 10.1.0 to 10.2.0 in /test/load (#4193)
Bumps [jsonpath-plus](https://github.com/s3u/JSONPath) from 10.1.0 to 10.2.0.
- [Release notes](https://github.com/s3u/JSONPath/releases)
- [Changelog](https://github.com/JSONPath-Plus/JSONPath/blob/main/CHANGES.md)
- [Commits](https://github.com/s3u/JSONPath/compare/v10.1.0...v10.2.0)

---
updated-dependencies:
- dependency-name: jsonpath-plus
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-02-06 21:32:39 -08:00
Gabe Kangas
92d75ddefc Update CI jobs to support PR branches (#4189)
* fix(ci): check out correct repo+ref for PRs. Addresses #3999

* fix(ci): update a bunch of other CI jobs to specify refs. For #3999
2025-02-06 19:48:56 -08:00
Owncast
9a99bfb0e9 Bundle embedded web app 2025-02-07 02:51:18 +00:00
Gabe Kangas
dd7a0ec081 fix(storybook): remove the storybook params causing render errors 2025-02-06 18:44:39 -08:00
renovate[bot]
071bb660dc chore(deps): update peter-evans/create-or-update-comment digest to 362dbaf 2025-02-05 08:35:49 +00:00
renovate[bot]
9529b12683 fix(deps): update module golang.org/x/time to v0.10.0 2025-02-05 02:04:43 +00:00
renovate[bot]
d2385aaab7 fix(deps): update module golang.org/x/mod to v0.23.0 2025-02-04 22:11:40 +00:00
renovate[bot]
448ef8e24f fix(deps): update module github.com/shirou/gopsutil/v4 to v4.25.1 (#4187)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-04 22:10:33 +00:00
renovate[bot]
982e293137 fix(deps): update module github.com/go-chi/chi/v5 to v5.2.1 2025-02-04 13:27:03 +00:00
Gabe Kangas
01fd73d1d1 fix(test): fix linter warnings in shell script 2025-01-31 19:25:30 -08:00
Gabe Kangas
7482a610b6 chore(test): add support for passing custom domain and port to test script 2025-01-31 19:22:48 -08:00
Owncast
b18f9ac75d Bundle embedded web app 2025-02-01 01:03:12 +00:00
Gabe Kangas
4744a27dd5 fix(admin): fix social engagement switch not showing correct state. Fixes #4184 2025-01-31 16:57:12 -08:00
Aziz Rmadi
8af820e60e fixed write header bug in case of errors in index html (#4185) 2025-01-30 21:21:51 -08:00
Gabe Kangas
8913779f81 Fix IndieAuth endpoint requiring incorrectly defined query param (#4183)
* fix(api): remove incorrectly required query param. Fixes #4163

* Commit updated API documentation

---------

Co-authored-by: Owncast <owncast@owncast.online>
2025-01-29 20:23:41 -08:00
Owncast
64df14c1df Bundle embedded web app 2025-01-30 02:31:30 +00:00
github-actions[bot]
436077a6f6 Updated translations (#4182)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2025-01-29 18:26:05 -08:00
renovate[bot]
213352414d chore(deps): update peter-evans/create-or-update-comment digest to 1a53cbe 2025-01-29 09:28:03 +00:00
renovate[bot]
dedb5c5a40 fix(deps): update module gopkg.in/evanphx/json-patch.v5 to v5.9.11 2025-01-28 22:14:23 +00:00
renovate[bot]
84178aa790 fix(deps): update module gopkg.in/evanphx/json-patch.v5 to v5.9.10 2025-01-27 21:33:48 +00:00
Owncast
a48867aee6 Bundle embedded web app 2025-01-27 02:59:09 +00:00
github-actions[bot]
b728bfc70a Updated translations (#4179) 2025-01-26 18:54:09 -08:00
Owncast
11af286501 Bundle embedded web app 2025-01-23 03:56:45 +00:00
github-actions[bot]
613d928149 Updated translations (#4176)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2025-01-22 19:51:53 -08:00
renovate[bot]
7ecc7d33d4 chore(deps): update peter-evans/create-or-update-comment digest to 2ef7be1 2025-01-22 06:37:22 +00:00
Owncast
9c8c18919f Bundle embedded web app 2025-01-22 01:40:34 +00:00
github-actions[bot]
69fd525b8d Updated translations (#4169)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2025-01-21 17:35:40 -08:00
Owncast
1df783d0ff Bundle embedded web app 2025-01-21 23:25:56 +00:00
dependabot[bot]
8a5afae48a chore(deps): bump undici from 6.21.0 to 6.21.1 in /web (#4173)
Bumps [undici](https://github.com/nodejs/undici) from 6.21.0 to 6.21.1.
- [Release notes](https://github.com/nodejs/undici/releases)
- [Commits](https://github.com/nodejs/undici/compare/v6.21.0...v6.21.1)

---
updated-dependencies:
- dependency-name: undici
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-21 15:20:48 -08:00
Owncast
15e0a2eb73 Bundle embedded web app 2025-01-21 07:34:12 +00:00
renovate[bot]
bf685c5702 fix(deps): update dependency yaml to v2.7.0 (#4172)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-20 23:29:02 -08:00
Owncast
d1fd6bc8cb Bundle embedded web app 2025-01-21 07:21:16 +00:00
renovate[bot]
6c8daab1bd chore(deps): update dependency typescript to v5.7.3 (#4171)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-20 23:16:05 -08:00
Owncast
3e1f06913f Bundle embedded web app 2025-01-21 07:12:47 +00:00
renovate[bot]
1e80c6dff8 chore(deps): update dependency chromatic to v11.25.0 (#4170)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-20 23:07:28 -08:00
Owncast
6482fc9f59 Bundle embedded web app 2025-01-21 03:45:30 +00:00
renovate[bot]
31d6fc94fe chore(deps): update dependency eslint-plugin-storybook to v0.11.2 (#4165)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-20 19:40:22 -08:00
Owncast
8e89c71a34 Bundle embedded web app 2025-01-21 03:24:24 +00:00
github-actions[bot]
1ad9376c4b Updated translations (#4168)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2025-01-20 19:19:37 -08:00
Owncast
3ddc5a8d50 Bundle embedded web app 2025-01-21 02:49:11 +00:00
github-actions[bot]
6fd3aad81d Updated translations (#4162)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2025-01-20 18:44:02 -08:00
Owncast
520183c39f Bundle embedded web app 2025-01-21 02:07:32 +00:00
renovate[bot]
86078d8b90 chore(deps): update dependency knip to v5.42.2 (#4166)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-20 18:02:35 -08:00
renovate[bot]
caa27d6fdb chore(deps): update dependency sass to v1.83.4 (#4167)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-20 18:01:32 -08:00
renovate[bot]
24b1dd2ed8 chore(deps): update dependency @types/node to v22.10.7 (#4164)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-20 18:01:03 -08:00
Gabe Kangas
c1f4096e11 chore(go): new chat message db repository. Closes #3081 (#4161) 2025-01-20 16:32:25 -08:00
Owncast
db0ddfe009 Bundle embedded web app 2025-01-21 00:04:05 +00:00
github-actions[bot]
9ea94e9963 Updated translations (#4160)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2025-01-20 15:58:45 -08:00
Owncast
c72ae603c1 Bundle embedded web app 2025-01-20 20:48:24 +00:00
github-actions[bot]
5e8373fcac Updated translations (#4156)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2025-01-20 12:43:32 -08:00
Owncast
b48b3cbc35 Bundle embedded web app 2025-01-20 02:57:04 +00:00
github-actions[bot]
515e378b4b Updated translations (#4153)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2025-01-19 18:52:08 -08:00
Owncast
583668f750 Bundle embedded web app 2025-01-19 23:20:23 +00:00
github-actions[bot]
d109c0cd7d Updated translations (#4152)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2025-01-19 15:15:29 -08:00
Owncast
7949670061 Bundle embedded web app 2025-01-19 22:07:46 +00:00
renovate[bot]
9a7a072050 chore(deps): update dependency eslint-plugin-react to v7.37.4 (#4144)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-19 14:02:25 -08:00
renovate[bot]
eb608414f6 chore(deps): update dependency eslint-plugin-prettier to v5.2.2 (#4143)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-19 14:00:57 -08:00
Owncast
0d38420a7a Bundle embedded web app 2025-01-19 21:50:27 +00:00
renovate[bot]
d81c148f68 chore(deps): update dependency stylelint to v16.13.2 (#4145)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-19 13:45:23 -08:00
Owncast
89b4e39542 Bundle embedded web app 2025-01-19 21:45:08 +00:00
github-actions[bot]
4ad771ac3d Updated translations (#4151)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2025-01-19 13:40:21 -08:00
Gabe Kangas
0e8ff0bea3 chore(i18n): update translation commit message 2025-01-19 13:31:20 -08:00
Gabe Kangas
ce976a5f0b chore(ci): tweak i18n workflows 2025-01-19 13:20:32 -08:00
Owncast
5e64b6ea41 Bundle embedded web app 2025-01-19 20:20:54 +00:00
github-actions[bot]
49f7c12b7e Updated translations (#4148)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2025-01-19 12:15:38 -08:00
Vibhanshu Jain
9dcee79432 Update translation.json (#4150) 2025-01-19 12:10:58 -08:00
Gabe Kangas
e78d62ce63 chore(go): move stream keys to use generated type. For #3778 2025-01-18 16:38:59 -08:00
Gabe Kangas
b3947ef7ea chore(go): move a couple more handlers to use generated types. For #3778 2025-01-18 16:24:35 -08:00
Gabe Kangas
6abbf8f50c chore(go): create webhooks repository. Closes #4085 (#4146) 2025-01-18 15:40:10 -08:00
Owncast
da9d5b8411 Bundle embedded web app 2025-01-18 18:18:07 +00:00
github-actions[bot]
05dd162de5 Updated translations (#4141)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2025-01-18 10:12:47 -08:00
Owncast
fc862b3fa0 Bundle embedded web app 2025-01-18 17:18:19 +00:00
renovate[bot]
6f8e9f9496 chore(deps): update dependency emoji-mart to v5.6.0 (#4139)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-18 09:13:09 -08:00
renovate[bot]
d6d126a874 chore(deps): update dependency chromatic to v11.24.0 (#4138)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-18 09:11:50 -08:00
renovate[bot]
555b305405 chore(deps): update dependency knip to v5.42.1 (#4140)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-18 09:11:29 -08:00
Owncast
58b13d3355 Bundle embedded web app 2025-01-18 08:58:40 +00:00
renovate[bot]
ff45f75731 fix(deps): update codemirror (#4136)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-18 00:53:42 -08:00
renovate[bot]
d9c97fb982 fix(deps): update dependency react-error-boundary to v5 (#4137)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-18 00:53:16 -08:00
renovate[bot]
3ab5702741 chore(deps): update dependency less to v4.2.1 (#4135)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-18 00:52:38 -08:00
Owncast
cc43d14199 Bundle embedded web app 2025-01-18 00:01:26 +00:00
dependabot[bot]
9c243f0ddf chore(deps): bump katex from 0.16.19 to 0.16.21 in /web (#4132)
Bumps [katex](https://github.com/KaTeX/KaTeX) from 0.16.19 to 0.16.21.
- [Release notes](https://github.com/KaTeX/KaTeX/releases)
- [Changelog](https://github.com/KaTeX/KaTeX/blob/main/CHANGELOG.md)
- [Commits](https://github.com/KaTeX/KaTeX/compare/v0.16.19...v0.16.21)

---
updated-dependencies:
- dependency-name: katex
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-17 15:56:19 -08:00
Owncast
4b39458bf1 Bundle embedded web app 2025-01-17 23:46:08 +00:00
Gabe Kangas
47857e283e fix(web): improve sizing and spacing of offline embed. Closes #4133 2025-01-17 15:40:06 -08:00
Owncast
6017d575c8 Bundle embedded web app 2025-01-17 19:58:35 +00:00
renovate[bot]
536eeb804a fix(deps): update dependency @uiw/codemirror-theme-bbedit to v4.23.7 (#4122)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-17 11:53:22 -08:00
Tianhao Wang
5c5e28a09c chore: remove binary blob (#4130)
I guess the .DS_Store is included by accident.. Let's remove it before it's
checked into a release tag.

Signed-off-by: Tianhao Wang <shrik3@mailbox.org>
2025-01-17 10:22:08 -08:00
Owncast
4690b13d6b Bundle embedded web app 2025-01-17 06:11:32 +00:00
renovate[bot]
78eec1e021 fix(deps): update dependency @uiw/react-codemirror to v4.23.7 (#4123)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-16 22:06:11 -08:00
Owncast
244dc2d7c1 Bundle embedded web app 2025-01-17 05:16:06 +00:00
renovate[bot]
03bac4ab48 fix(deps): update dependency react-markdown to v9.0.3 (#4125)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-16 21:11:00 -08:00
renovate[bot]
2d4ae6ca20 chore(deps): update dependency @types/react to v18.3.18 (#4124)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-16 21:10:26 -08:00
Owncast
2a4f53eb54 Bundle embedded web app 2025-01-17 03:51:09 +00:00
renovate[bot]
d494856ca2 chore(deps): update dependency @types/prop-types to v15.7.14 (#4121)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-16 19:44:34 -08:00
renovate[bot]
e8a5ff95d4 fix(deps): update dependency react-virtuoso to v4.12.3 (#4126)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-16 19:42:42 -08:00
renovate[bot]
a282daa12f fix(deps): update dependency ua-parser-js to v1.0.40 (#4127)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-16 19:41:49 -08:00
renovate[bot]
2ff4baeb18 chore(deps): update dependency @types/node to v22.10.6 (#4128)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-16 19:40:47 -08:00
renovate[bot]
96ae5c7dce fix(deps): update module github.com/aws/aws-sdk-go to v1.55.6 (#4116)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-16 14:33:41 +00:00
Owncast
d328fe1fd8 Bundle embedded web app 2025-01-16 07:40:52 +00:00
Gabe Kangas
8ab8659889 fix(i18n): next-export-i18n should be using the 2.x.x branch 2025-01-15 23:32:16 -08:00
Owncast
5733c86d8f Bundle embedded web app 2025-01-16 06:09:16 +00:00
Owncast
a1d94a7f99 Commit updated API documentation 2025-01-16 06:05:39 +00:00
Gabe Kangas
c1366518ad chore: bump version number 2025-01-15 22:02:48 -08:00
Gabe Kangas
bd8dc8326c fix(admin): do not log newfeed 404s 2025-01-15 22:00:32 -08:00
Gabe Kangas
d03cac106c fix(admin): do not handle version upgrade logic if current version is not set 2025-01-15 21:59:45 -08:00
Owncast
1c69164a72 Bundle embedded web app 2025-01-16 00:20:30 +00:00
Gabe Kangas
2e8e61309a Remove use of default props (#4118)
* refactor: replace defaultProps with function parameters in Modal, Statusbar, ChatContainer, and CrossfadeImage components

* New commit for Default properties of React components after syncing fork and rebasing

* fix: fix linter warning

---------

Co-authored-by: swarup <swarupnarkhede999@gmail.com>
2025-01-15 16:12:54 -08:00
Owncast
67ef2b45d9 Bundle embedded web app 2025-01-15 22:54:08 +00:00
Gabe Kangas
91a635a5ca chore(i18n): enable languages 2025-01-15 14:48:54 -08:00
github-actions[bot]
088023cf15 Updated translations (#4117)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2025-01-15 14:41:18 -08:00
Gabe Kangas
0f48bc4904 chore(i18n): run workflow every hour + download translations for PR 2025-01-15 14:18:37 -08:00
Owncast
e4aa69dc46 Bundle embedded web app 2025-01-15 22:04:41 +00:00
Gabe Kangas
dc8ed52b58 fix(i18n): disable languages until the files exist 2025-01-15 13:59:35 -08:00
Gabe Kangas
648856dcd1 chore: update i18n workflow/config 2025-01-15 13:41:44 -08:00
renovate[bot]
a4ed2c14be chore(deps): update peter-evans/create-or-update-comment digest to fdb73c4 2025-01-15 05:33:32 +00:00
Owncast
8e2e05e48e Bundle embedded web app 2025-01-15 05:32:40 +00:00
Gabe Kangas
d77b80a94a fix(storybook): fix another story not rendering with i18n 2025-01-14 21:27:21 -08:00
Owncast
bd59d8ab40 Bundle embedded web app 2025-01-15 05:25:05 +00:00
Gabe Kangas
70282761d3 fix(storybook): fix storybook rendering with i18n library 2025-01-14 21:19:23 -08:00
Owncast
e02f6dbc20 Bundle embedded web app 2025-01-15 04:59:24 +00:00
Gabe Kangas
cb387d88be Initial localization work (#3980)
* First pass at configuring localization

* Add CI job for translations

* Update CI job

* Update default value

* Update parser config

* Update defaults again

* try to fix the multiple parsing of a file

* Update crowdlin config

* Update configs

* New Crowdin translations by GitHub Action (#3448)

Co-authored-by: Crowdin Bot <support+bot@crowdin.com>

* Point to updated translated files

* Tooltip i18n

* Run translation job when web components are updated

* Commit updated translations

* Translations update (#3453)

* Update source file strings.json
Updated translations

* New translations strings.json (French)
Updated translations

* New translations strings.json (Spanish)
Updated translations

* New translations strings.json (German)
Updated translations

* New translations strings.json (English, United States)
Updated translations

* Commit updated translations

* New Crowdin translations by GitHub Action (#3452)

Co-authored-by: Owncast <owncast@owncast.online>

* chore(deps): update to next config to address build errors

* New Crowdin translations by GitHub Action (#3455)

Co-authored-by: Crowdin Bot <support+bot@crowdin.com>

* Translations update (#3456)

* New translations strings.json (Arabic)
Updated translations

* New translations strings.json (German)
Updated translations

* New translations strings.json (Greek)
Updated translations

* New translations strings.json (Irish)
Updated translations

* New translations strings.json (Italian)
Updated translations

* New translations strings.json (Japanese)
Updated translations

* New translations strings.json (Korean)
Updated translations

* New translations strings.json (Dutch)
Updated translations

* New translations strings.json (Norwegian)
Updated translations

* New translations strings.json (Punjabi)
Updated translations

* New translations strings.json (Russian)
Updated translations

* New translations strings.json (Swedish)
Updated translations

* New translations strings.json (Chinese Traditional)
Updated translations

* New translations strings.json (Vietnamese)
Updated translations

* New translations strings.json (Bengali)
Updated translations

* New translations strings.json (Thai)
Updated translations

* New translations strings.json (Croatian)
Updated translations

* New translations strings.json (Hindi)
Updated translations

* New translations strings.json (Malay)
Updated translations

* New Crowdin translations by GitHub Action (#3457)

* New translations strings.json (Arabic)
Updated translations

* New translations strings.json (German)
Updated translations

* New translations strings.json (Greek)
Updated translations

* New translations strings.json (Irish)
Updated translations

* New translations strings.json (Italian)
Updated translations

* New translations strings.json (Japanese)
Updated translations

* New translations strings.json (Korean)
Updated translations

* New translations strings.json (Dutch)
Updated translations

* New translations strings.json (Norwegian)
Updated translations

* New translations strings.json (Punjabi)
Updated translations

* New translations strings.json (Russian)
Updated translations

* New translations strings.json (Swedish)
Updated translations

* New translations strings.json (Chinese Traditional)
Updated translations

* New translations strings.json (Vietnamese)
Updated translations

* New translations strings.json (Bengali)
Updated translations

* New translations strings.json (Thai)
Updated translations

* New translations strings.json (Croatian)
Updated translations

* New translations strings.json (Hindi)
Updated translations

* New translations strings.json (Malay)
Updated translations

* New Crowdin translations by GitHub Action

---------

Co-authored-by: Gabe Kangas <gabek@real-ity.com>
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>

* Commit updated API documentation

* Update translations job

* New Crowdin translations by GitHub Action (#3698)

Co-authored-by: Crowdin Bot <support+bot@crowdin.com>

* Update Crowdin configuration file

* Translations update (#3700)

* New translations strings.json (French)
Updated translations

* New translations strings.json (Italian)
Updated translations

* Translations update (#3699)

* New translations strings.json (French)
Updated translations

* New translations strings.json (Spanish)
Updated translations

* New translations strings.json (Italian)
Updated translations

* New translations strings.json (Japanese)
Updated translations

* New translations strings.json (Polish)
Updated translations

* New translations strings.json (Russian)
Updated translations

* New translations strings.json (Portuguese, Brazilian)
Updated translations

* Commit updated API documentation

---------

Co-authored-by: Owncast <owncast@owncast.online>

* New Crowdin translations by GitHub Action (#3701)

* New translations strings.json (French)
Updated translations

* New translations strings.json (Spanish)
Updated translations

* New translations strings.json (Italian)
Updated translations

* New translations strings.json (Japanese)
Updated translations

* New translations strings.json (Polish)
Updated translations

* New translations strings.json (Russian)
Updated translations

* New translations strings.json (Portuguese, Brazilian)
Updated translations

* New Crowdin translations by GitHub Action

---------

Co-authored-by: Gabe Kangas <gabek@real-ity.com>
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>

* Draft: Mark strings for translation. (#3458)

* Mark strings for translation.

* Mark up strings for translation

* fix(web): fix linter warnings

---------

Co-authored-by: Le fractal <17422-fractal@users.noreply.framagit.org>
Co-authored-by: Gabe Kangas <gabek@real-ity.com>

* do not pull from cowdin via workflow

* Commit updated translations

* feat: add translations support to admin pages and components (#3977)

* feat: add translations support to admin pages and components

Added translations support admin main page and its components, help
page, handware-info page. Added translations support for LogTable,
NewsFeed and StreamHealthOverview components.

* update package.json

* fix rendering issue

* Commit updated API documentation

---------

Co-authored-by: Owncast <owncast@owncast.online>
Co-authored-by: Gabe Kangas <gabek@real-ity.com>

* Offline banner i18n formatting (#3997)

* Fix "Last live ago" string formatting with i18n interpolation

* Change some base translation jsons to use i18n interpolation

* Linting fix

* chore(js): ignore i18n pkgs in knip

* fix(test): fix browser ui test

* fix(js): remove unused var

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
Co-authored-by: Owncast <owncast@owncast.online>
Co-authored-by: taintedcypher <119351153+taintedcypher@users.noreply.github.com>
Co-authored-by: Le fractal <17422-fractal@users.noreply.framagit.org>
Co-authored-by: Sufyaan Khateeb <81009832+SufyaanKhateeb@users.noreply.github.com>
Co-authored-by: mahmed2000 <mahmad2000@protonmail.com>
2025-01-14 20:54:21 -08:00
Gabe Kangas
b45552ade0 fix(video): remove persistent HTTP connection. Hopefully fixes #4106 2025-01-14 08:37:27 -08:00
Gabe Kangas
2b42ff5ce4 Revert "fix(js): do not fire dangerouslySetInnerHTML if there is nothing to render. Closes #4075"
This reverts commit d91c42bafd9136805fd64018f0db517156bcb263.

Closes #4112
2025-01-14 08:33:03 -08:00
Owncast
a9e4ad55e9 Bundle embedded web app 2025-01-13 12:36:22 +00:00
renovate[bot]
5ccdcc7a9b chore(deps): lock file maintenance (#4109)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-13 12:30:54 +00:00
Owncast
271d9c3b87 Commit updated API documentation 2025-01-13 04:41:40 +00:00
Gabe Kangas
0d46f4a7d0 chore: bump version number to v0.2.1 2025-01-12 20:39:37 -08:00
Martin Wimpress
8ce270b02f feat: enable Intel Quicksync with support for FFmpeg 4.x to 7.1 (#4028)
* feat: enable Intel Quicksync with support for FFmpeg 4.x to 7.1

* fix(go): fix linter warning

---------

Co-authored-by: Gabe Kangas <gabek@real-ity.com>
2025-01-12 20:38:26 -08:00
Martin Wimpress
bb4a7479b5 fix: universal Intel and AMD VA-API accelerated pipeline for FFmpeg 4 - 7 (#4022) 2025-01-12 20:10:00 -08:00
renovate[bot]
0105473350 chore(deps): update alpine docker tag to v3.21.2 2025-01-09 00:20:50 +00:00
Owncast
191cf5ee6a Bundle embedded web app 2025-01-09 00:19:59 +00:00
Gabe Kangas
a669686b16 fix(js): move require to top of file 2025-01-08 16:14:25 -08:00
Gabe Kangas
b52bbc7f53 fix(admin): fix version update prompt. Fixes #4097 2025-01-08 15:40:33 -08:00
Owncast
591da94daf Bundle embedded web app 2025-01-08 23:15:32 +00:00
Gabe Kangas
048acbce88 fix(js): do not fire dangerouslySetInnerHTML if there is nothing to render. Closes #4075 (#4076) 2025-01-08 15:10:35 -08:00
renovate[bot]
0140601335 chore(deps): update peter-evans/create-or-update-comment digest to 853a4fc 2025-01-08 08:22:01 +00:00
renovate[bot]
768c9ab9b3 chore(deps): update alpine docker tag to v3.21.1 2025-01-07 09:01:53 +00:00
renovate[bot]
7cfecef39c fix(deps): update module golang.org/x/net to v0.34.0 2025-01-06 21:49:23 +00:00
Gabe Kangas
2d4b5eb105 Do not mark as stale if it is assigned to a milestone 2025-01-06 13:48:06 -08:00
renovate[bot]
790e148050 fix(deps): update module golang.org/x/crypto to v0.32.0 2025-01-06 17:22:48 +00:00
Owncast
dc4562db66 Bundle embedded web app 2025-01-06 13:11:59 +00:00
renovate[bot]
327feaafac chore(deps): lock file maintenance (#4084)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-06 13:06:11 +00:00
Owncast
d752f51034 Bundle embedded web app 2025-01-06 01:33:33 +00:00
gingervitis
87c7571d5c embed screen style adjustments (#4063)
* restyle and relayout embed screen to account for smaller screen displays.
- address https://github.com/owncast/owncast/issues/3683 to address overflow issues
- address https://github.com/owncast/owncast/issues/4051 to move the name of the stream

* Javascript formatting autofixes

* clean up; restore package lock

* accommodate cases when there's no follow option; put follow form on one line, but wrap if need

* clean up

* separate out follow form into separate standalone component to be used in multiple places

* improve follow error styling; rm defaultProps for Modal to get rid of warning

* improve styling of follow form and components for legibility

* prettyify scss

* prettyify scss again

* one more time

* prettify ant file

* simplify layout, center everything

* just use gap

* tweak and lint

* lint, again

---------

Co-authored-by: Owncast <owncast@owncast.online>
2025-01-05 17:28:35 -08:00
Owncast
4b1a89bb31 Bundle embedded web app 2025-01-06 00:40:18 +00:00
Andy Piper
9f820b00c1 Update mastodon.svg for current colors (#4082) 2025-01-05 16:35:17 -08:00
Owncast
18875f3f2a Bundle embedded web app 2025-01-05 20:27:13 +00:00
Germaine Lee
c23655c210 Update some platform logos (#4081)
* Update discord logo

Discord updated their branding in 2021.
See their brand guidelines: https://discord.com/branding

* Update ko-fi logo to rounded cup

See their website for more details: https://more.ko-fi.com/brand-assets

* Update paypal logo

See Paypal brand logos at: https://newsroom.paypal-corp.com/media-resources
2025-01-05 12:22:20 -08:00
Owncast
3510d19fce Bundle embedded web app 2025-01-05 04:29:29 +00:00
gingervitis
6528633a13 fix skip link visibility and it moving logo upon focus (#4078)
* fix skip link visibility and it moving logo upon focus

* update color
2025-01-04 20:24:27 -08:00
Owncast
678214c19d Bundle embedded web app 2025-01-04 23:13:28 +00:00
renovate[bot]
6355ab3b5f fix(deps): update fontsource monorepo to v5.1.1 (#4074)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-04 15:08:15 -08:00
Owncast
d9da9a3481 Bundle embedded web app 2025-01-04 22:29:34 +00:00
renovate[bot]
4b40ed6a93 fix(deps): update dependency next to v14.2.21 [security] (#4071)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-04 14:24:40 -08:00
Owncast
510cae1b79 Bundle embedded web app 2025-01-04 21:04:36 +00:00
gingervitis
a6dc9645e6 Add local CSS lint checks (#4068)
* update package scripts to include prettifying scss files and stylelint-ing; fix related issues found from stylelinter

* revert old prettier autofix

* lint

---------

Co-authored-by: Gabe Kangas <gabek@real-ity.com>
2025-01-04 12:59:45 -08:00
renovate[bot]
c03637fd9b chore(deps): update dependency stylelint-config-standard-scss to v14 (#4070)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-04 12:57:02 -08:00
renovate[bot]
bdd25498a8 fix(deps): update module golang.org/x/time to v0.9.0 2025-01-04 16:33:10 +00:00
Owncast
3e7cd32dfa Bundle embedded web app 2025-01-04 01:51:58 +00:00
renovate[bot]
17ee4bba03 chore(deps): update dependency prettier to v3.4.2 (#4069)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-03 17:46:21 -08:00
renovate[bot]
9e8088fa80 fix(deps): update module github.com/sherclockholmes/webpush-go to v1.4.0 2025-01-03 00:12:45 +00:00
renovate[bot]
009a271c02 fix(deps): update module mvdan.cc/xurls/v2 to v2.6.0 2025-01-02 19:24:02 +00:00
renovate[bot]
092b76b449 chore(deps): update peter-evans/create-or-update-comment digest to 4ed2ee4 2025-01-01 06:17:18 +00:00
renovate[bot]
3bc3799760 fix(deps): update module github.com/shirou/gopsutil/v4 to v4.24.12 2025-01-01 03:50:56 +00:00
Owncast
6b97962da0 Bundle embedded web app 2024-12-23 13:33:14 +00:00
renovate[bot]
262bfd6c41 chore(deps): lock file maintenance (#4064)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-23 13:28:14 +00:00
Owncast
6057fb09d7 Bundle embedded web app 2024-12-20 17:22:34 +00:00
mahmed2000
8a1c05abf4 Make the error alert in OfflineEmbed closable (#4062) 2024-12-20 09:17:34 -08:00
renovate[bot]
0b28a3b483 fix(deps): update module github.com/andybalholm/cascadia to v1.3.3 2024-12-19 18:48:50 +00:00
renovate[bot]
9c745b0ef1 fix(deps): update module golang.org/x/net to v0.33.0 2024-12-18 21:40:17 +00:00
Gabe Kangas
a38c2d8ddf fix: add ETag header for bot/scraper page. For #4020 2024-12-18 07:28:06 -08:00
Gabe Kangas
f59278764b fix: bump bot/webscraper server generated page cache to 1 minute. For #4020 2024-12-18 07:19:31 -08:00
renovate[bot]
852dd61810 chore(deps): update peter-evans/create-or-update-comment digest to 861c151 2024-12-18 07:22:30 +00:00
Gabe Kangas
4fbdb3f0cd fix(ap): increase outbound worker pool size to use follower count (#4049) 2024-12-17 08:47:15 -08:00
Owncast
ae1be1379c Bundle embedded web app 2024-12-16 13:07:51 +00:00
renovate[bot]
8ecd2a2bd1 chore(deps): lock file maintenance (#4057)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-16 13:02:28 +00:00
renovate[bot]
37d8a0e879 fix(deps): update module github.com/go-chi/chi/v5 to v5.2.0 2024-12-16 00:42:46 +00:00
Owncast
8ddf3b27fa Bundle embedded web app 2024-12-13 06:49:03 +00:00
Gabe Kangas
e1a48909c8 fix(embed): fix follow button not showing up in offline embed. Closes #4053 2024-12-12 22:43:30 -08:00
Owncast
c8d8c8e07c Bundle embedded web app 2024-12-13 06:39:26 +00:00
Gabe Kangas
8ffbf23d4e fix(js): fix social switch not actually turning on feature. Closes #4055 2024-12-12 22:33:37 -08:00
Owncast
06bd9e849f Bundle embedded web app 2024-12-13 05:45:06 +00:00
Gabe Kangas
446d5db085 fix(js): fix followers table being empty when sorted. Closes #3799 (#4054) 2024-12-12 21:40:24 -08:00
Gabe Kangas
3bbbca9465 Remove nightly screenshot job. Re: #3999 2024-12-12 12:14:49 -08:00
renovate[bot]
c86b7cff1b fix(deps): update module golang.org/x/crypto to v0.31.0 2024-12-11 18:49:50 +00:00
dependabot[bot]
9807f60b1b Bump nanoid from 3.3.7 to 3.3.8 in /test/load (#4052)
Bumps [nanoid](https://github.com/ai/nanoid) from 3.3.7 to 3.3.8.
- [Release notes](https://github.com/ai/nanoid/releases)
- [Changelog](https://github.com/ai/nanoid/blob/main/CHANGELOG.md)
- [Commits](https://github.com/ai/nanoid/compare/3.3.7...3.3.8)

---
updated-dependencies:
- dependency-name: nanoid
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-11 09:19:02 -08:00
renovate[bot]
a5f25299b5 chore(deps): update peter-evans/create-or-update-comment digest to d7d9eb5 2024-12-11 08:01:06 +00:00
Owncast
1a4819564d Bundle embedded web app 2024-12-10 01:32:35 +00:00
dependabot[bot]
d1124182af Bump nanoid from 3.3.7 to 3.3.8 in /web (#4050)
Bumps [nanoid](https://github.com/ai/nanoid) from 3.3.7 to 3.3.8.
- [Release notes](https://github.com/ai/nanoid/releases)
- [Changelog](https://github.com/ai/nanoid/blob/main/CHANGELOG.md)
- [Commits](https://github.com/ai/nanoid/compare/3.3.7...3.3.8)

---
updated-dependencies:
- dependency-name: nanoid
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-09 17:27:23 -08:00
renovate[bot]
281a93b49d fix(deps): update module github.com/grafov/m3u8 to v0.12.1 (#4048)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-09 23:13:16 +00:00
Gabe Kangas
c24ee6202a Update local tunneling script to no longer use serveo.net 2024-12-09 10:37:47 -08:00
renovate[bot]
4f1c1ec683 chore(deps): update alpine docker tag to v3.21.0 2024-12-06 01:19:20 +00:00
renovate[bot]
122556c440 fix(deps): update module golang.org/x/net to v0.32.0 2024-12-05 00:47:38 +00:00
renovate[bot]
4bdcbb6d0b fix(deps): update module golang.org/x/crypto to v0.30.0 2024-12-04 21:32:54 +00:00
renovate[bot]
26ceac7069 chore(deps): update peter-evans/create-or-update-comment digest to 1abf947 2024-12-04 08:31:53 +00:00
Gabe Kangas
c5ac3a30af fix: pass in config repo instead of using the global getter (#4039) 2024-11-30 23:38:27 -08:00
renovate[bot]
d8aed658a8 fix(deps): update module github.com/shirou/gopsutil/v4 to v4.24.11 2024-12-01 04:24:50 +00:00
Owncast
1777fb9a37 Bundle embedded web app 2024-11-30 22:42:51 +00:00
mahmed2000
d9a0d13479 Communicate and handle apub outgoing updates being delayed (#4009)
* Allow icon only status messages such as STATUS_PROCESSING to be displayed

* Add a processing status state for the EditSocialLinks component

* Log warning for the outbound apub channel being full

* Buffer the outbound apub channel so some API requests are less likely to get blocked during handling

* Make the apub outbound request trace-log always occur after being queued.

* Linting fix
2024-11-30 14:38:00 -08:00
Owncast
df028f90cf Bundle embedded web app 2024-11-30 20:37:06 +00:00
Varun Gujarathi
f59ac63dfe fix(social): set federation enabled state after confirmation (#4038)
* feat(integerations): add route to get user details

* Commit updated API documentation

* test(integrations): implement unit test for get user details

* fix(social): set federation enabled state after confirmation

set federation enabled state only after confirmation in modal

* Javascript formatting autofixes

* chore(social): use variable instead of explicitly setting path value

* Javascript formatting autofixes

---------

Co-authored-by: Owncast <owncast@owncast.online>
Co-authored-by: Gabe Kangas <gabek@real-ity.com>
2024-11-30 12:32:13 -08:00
renovate[bot]
ff1f201798 chore(deps): update peter-evans/create-or-update-comment digest to 267c060 2024-11-27 06:05:32 +00:00
Varun Gujarathi
d135d2907a feat(integerations): add route to get user details (#4030)
* feat(integerations): add route to get user details

* Commit updated API documentation

* test(integrations): implement unit test for get user details

---------

Co-authored-by: Owncast <owncast@owncast.online>
Co-authored-by: Gabe Kangas <gabek@real-ity.com>
2024-11-26 11:30:33 -08:00
Owncast
2c2bf2b5bb Bundle embedded web app 2024-11-25 14:10:56 +00:00
renovate[bot]
32bf67b026 chore(deps): lock file maintenance (#4035)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-25 14:05:57 +00:00
renovate[bot]
d578f01348 fix(deps): update module github.com/stretchr/testify to v1.10.0 2024-11-23 12:22:10 +00:00
renovate[bot]
8985947d7f chore(deps): update peter-evans/create-or-update-comment digest to 7157823 2024-11-20 07:57:56 +00:00
Owncast
e1acfee49d Bundle embedded web app 2024-11-18 09:18:37 +00:00
renovate[bot]
49c07594fb chore(deps): lock file maintenance (#4018)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-18 09:13:30 +00:00
dependabot[bot]
dc48e0bca7 Bump jsonpath-plus and artillery in /test/load (#4011)
Bumps [jsonpath-plus](https://github.com/s3u/JSONPath) to 10.1.0 and updates ancestor dependency [artillery](https://github.com/artilleryio/artillery). These dependencies need to be updated together.


Updates `jsonpath-plus` from 7.2.0 to 10.1.0
- [Release notes](https://github.com/s3u/JSONPath/releases)
- [Changelog](https://github.com/JSONPath-Plus/JSONPath/blob/main/CHANGES.md)
- [Commits](https://github.com/s3u/JSONPath/compare/v7.2.0...v10.1.0)

Updates `artillery` from 2.0.10 to 2.0.21
- [Release notes](https://github.com/artilleryio/artillery/releases)
- [Commits](https://github.com/artilleryio/artillery/compare/artillery-2.0.10...artillery-2.0.21)

---
updated-dependencies:
- dependency-name: jsonpath-plus
  dependency-type: indirect
- dependency-name: artillery
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-15 20:04:45 -08:00
Gabe Kangas
0b5d7c8a4d Config repository (#3988)
* WIP

* fix(test): fix ap test failing

* fix: fix unkeyed fields being used

* chore(tests): clean up browser tests by splitting out federation UI tests
2024-11-15 19:20:58 -08:00
Gabe Kangas
56d52c283c chore(tests): clean up browser tests by splitting out federation UI tests 2024-11-15 18:46:18 -08:00
Owncast
8fa2546bef Bundle embedded web app 2024-11-15 21:31:12 +00:00
Gabe Kangas
70bbcb97b8 fix(js): tweak how the vjs hook is fired and url is created 2024-11-15 13:25:06 -08:00
Owncast
d8abe74e5a Bundle embedded web app 2024-11-15 21:00:08 +00:00
Mahlangu
681111ceb2 refactor: update vjs beforeRequest to onRequest (#3945)
* refactor: update vjs onRequst to onRequest

* fix(js): update vjs to use the new onrequest hook for manipulating request uri

---------

Co-authored-by: Gabe Kangas <gabek@real-ity.com>
2024-11-15 12:55:26 -08:00
Owncast
284833d6a0 Commit updated API documentation 2024-11-15 20:24:14 +00:00
Gabe Kangas
a566b9c9f1 fix(api): define correct inbound api payloads for external requests. Fixes #3966 2024-11-15 12:22:18 -08:00
Owncast
621aebdd3c Commit updated API documentation 2024-11-14 21:44:33 +00:00
Gabe Kangas
2be188a82f Remove API from incorrectly listed as external. For #4015 2024-11-14 13:42:45 -08:00
Owncast
e17b443726 Bundle embedded web app 2024-11-14 18:41:37 +00:00
renovate[bot]
dfc934ce84 chore(deps): update dependency knip to v5.36.6 (#4013)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-14 10:36:35 -08:00
renovate[bot]
512e4dc575 fix(deps): update dependency date-fns to v4 (#4014)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-14 10:36:10 -08:00
Owncast
421cdd0693 Bundle embedded web app 2024-11-13 16:16:31 +00:00
Gabe Kangas
df06d9ad97 fix(deps): revert emoji-mart to v5.2.2 again 2024-11-13 08:10:45 -08:00
Gabe Kangas
7f42981ba7 chore(deps): add slashes to renovate config regex 2024-11-13 08:09:30 -08:00
Owncast
f3029ca782 Bundle embedded web app 2024-11-12 23:00:59 +00:00
renovate[bot]
58657804cb chore(deps): update dependency emoji-mart to v5.6.0 2024-11-12 20:44:59 +00:00
renovate[bot]
d21b10f118 fix(deps): update module github.com/twin/go-away to v1.6.14 2024-11-12 20:07:28 +00:00
renovate[bot]
ae48128441 chore(config): migrate config renovate.json (#4012)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-12 12:05:52 -08:00
Gabe Kangas
b278027b88 chore(deps): ignore golang and go-fed when updating go deps 2024-11-12 08:43:45 -08:00
renovate[bot]
f45a5b16fa fix(deps): update github.com/go-fed/activity digest to b14b50e 2024-11-12 16:28:25 +00:00
Owncast
f3d660853c Bundle embedded web app 2024-11-12 16:26:43 +00:00
Gabe Kangas
aecf7aa9bb fix(deps): do not allow statically assigned versions to auto-update. See #4010 2024-11-12 08:21:04 -08:00
Owncast
37c18ec8ab Bundle embedded web app 2024-11-12 02:11:50 +00:00
renovate[bot]
4c7ccdbbf9 chore(deps): update dependency emoji-mart to v5.6.0 2024-11-11 21:43:11 +00:00
Owncast
a20a9931f7 Bundle embedded web app 2024-11-11 21:42:28 +00:00
Gabe Kangas
b177333ec6 fix(web): hardcode emoji-mart picker version to a working version. Fixes #4010 2024-11-11 13:36:15 -08:00
Owncast
5042c7ced2 Bundle embedded web app 2024-11-11 13:02:23 +00:00
renovate[bot]
71c88c94a3 chore(deps): lock file maintenance (#4008)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-11 12:57:29 +00:00
renovate[bot]
41a6449836 chore(deps): update dependency chromatic to v11.18.0 2024-11-11 01:35:04 +00:00
Owncast
b656f88417 Bundle embedded web app 2024-11-10 22:07:19 +00:00
renovate[bot]
ee5dc3b7c8 chore(deps): update dependency chromatic to v11.17.0 2024-11-10 18:05:15 +00:00
Owncast
1476405609 Bundle embedded web app 2024-11-10 01:42:18 +00:00
renovate[bot]
7830b3765a chore(deps): update dependency emoji-mart to ~5.6.0 2024-11-09 22:24:06 +00:00
Owncast
f9f61876df Bundle embedded web app 2024-11-09 22:23:11 +00:00
Rafael Passos
17d433749c fix: registerUser request body is invalid (#4002)
I found two issues:
1. This `options` object is being passed down to fetchData,
 where it is deconstructed by the "data" attribute, not body.
2. the `data` object is being transformed into JSON downstream,
 thus the stringification done here makes for a string json object only

Signed-off-by: auyer <rafael@rcpassos.me>
2024-11-09 14:18:05 -08:00
Owncast
9989653d00 Bundle embedded web app 2024-11-09 22:13:50 +00:00
heongle
6399df7f9e fix: fix runtime caching error and adjust rules (#3970)
* fix: fix runtime caching error and adjust rules

* fix: add cache config for ts and m3u8

* revert ts and m3u8 cache config to previous behaviour

* remove unused runtime caching config

* remove all runtime caching config

---------

Co-authored-by: Gabe Kangas <gabek@real-ity.com>
2024-11-09 14:08:45 -08:00
Owncast
99acc19cee Bundle embedded web app 2024-11-09 22:03:32 +00:00
mahmed2000
eca880ac1f Replace picmo with emoji-mart (#4001)
* Add emoji-mart deps

* Change EmojiPicker to use emoji-mart

* Change ChatTextField to work with the emoji-mart data object

* Remove picmo, commit package-lock

* Fix mutant svgs having a size of 0

* Get the custom emojis to show up earlier in the picker

* Set emoji-mart to exact semver. Later versions break custom category sorting.
2024-11-09 13:58:38 -08:00
renovate[bot]
f215809f1d fix(deps): update module golang.org/x/time to v0.8.0 2024-11-09 20:12:15 +00:00
renovate[bot]
77d2bacbad fix(deps): update module golang.org/x/mod to v0.22.0 (#4006)
* fix(deps): update module golang.org/x/mod to v0.22.0

* chore: go mod tidy

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Gabe Kangas <gabek@real-ity.com>
2024-11-09 12:11:29 -08:00
renovate[bot]
1b1144c6df fix(deps): update module golang.org/x/net to v0.31.0 (#4007)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-09 15:08:38 +00:00
renovate[bot]
971e3ea092 fix(deps): update module golang.org/x/crypto to v0.29.0 (#4005)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-09 15:07:31 +00:00
Owncast
24e2552e4c Bundle embedded web app 2024-11-09 12:20:28 +00:00
renovate[bot]
56f6aa8add fix(deps): update dependency @codemirror/lang-markdown to v6.3.1 2024-11-09 10:29:09 +00:00
Owncast
de8579bcd6 Bundle embedded web app 2024-11-09 10:28:04 +00:00
renovate[bot]
56ede52346 chore(deps): update dependency knip to v5.36.3 2024-11-09 07:56:46 +00:00
Owncast
8269ae3209 Bundle embedded web app 2024-11-09 07:56:04 +00:00
renovate[bot]
674c9168ec fix(deps): update nextjs monorepo to v14.2.17 2024-11-09 04:45:09 +00:00
Owncast
801e91d2f1 Bundle embedded web app 2024-11-09 04:43:25 +00:00
renovate[bot]
1e1dc0ff49 chore(deps): update dependency cypress to v13.15.2 (#4004)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-08 20:38:38 -08:00
Owncast
fb75f1bd95 Bundle embedded web app 2024-11-09 03:24:44 +00:00
renovate[bot]
af56597341 chore(deps): update dependency mdx-mermaid to v2.0.2 2024-11-09 01:04:32 +00:00
Owncast
214c202552 Bundle embedded web app 2024-11-09 01:01:59 +00:00
renovate[bot]
ad64956027 chore(deps): update dependency @types/node to v22 (#4003)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-08 16:57:04 -08:00
renovate[bot]
13d2ad9079 fix(deps): update module github.com/shirou/gopsutil/v4 to v4.24.10 2024-11-08 21:35:24 +00:00
Gabe Kangas
83ef466ee1 fix(ci): check out repo if event_name is schedule 2024-11-08 13:16:13 -08:00
Owncast
a8026c13bc Bundle embedded web app 2024-11-08 19:48:39 +00:00
mahmed2000
d31d2948c3 Change websocket mock from empty object to empty class (#3998) 2024-11-08 11:43:41 -08:00
Owncast
4d68c7b561 Bundle embedded web app 2024-11-08 18:10:46 +00:00
Gabe Kangas
25cd9b6d53 fix(js): log out unused error value 2024-11-08 10:05:53 -08:00
Owncast
ecba3cc003 Bundle embedded web app 2024-11-08 09:09:49 +00:00
renovate[bot]
89dc1776d7 chore(deps): update storybook monorepo to v8.4.2 2024-11-08 07:11:30 +00:00
Owncast
97fcdfd914 Bundle embedded web app 2024-11-08 03:11:13 +00:00
renovate[bot]
0af864ea8f chore(deps): update dependency chromatic to v11.16.5 2024-11-08 01:54:42 +00:00
Owncast
121cd37c8c Bundle embedded web app 2024-11-08 01:53:07 +00:00
renovate[bot]
06c09f12de chore(deps): update typescript-eslint monorepo to v8.13.0 2024-11-07 21:26:36 +00:00
Owncast
d1117494b0 Bundle embedded web app 2024-11-07 21:24:20 +00:00
renovate[bot]
68a903b83a chore(deps): update dependency eslint-plugin-storybook to v0.11.0 2024-11-07 19:41:56 +00:00
Owncast
c8c91506cd Bundle embedded web app 2024-11-07 19:39:56 +00:00
renovate[bot]
a1a497f5c1 chore(deps): update dependency chromatic to v11.16.4 2024-11-07 16:42:28 +00:00
Owncast
219f76d891 Bundle embedded web app 2024-11-06 14:27:12 +00:00
renovate[bot]
ad9b01f442 chore(deps): update dependency @types/node to v20.17.6 2024-11-06 11:06:34 +00:00
renovate[bot]
962d03710d chore(deps): update peter-evans/create-or-update-comment digest to 213b1f9 2024-11-06 06:21:32 +00:00
dependabot[bot]
2ebc64b3b1 Bump cookie and cookie-parser in /test/load (#3996)
Bumps [cookie](https://github.com/jshttp/cookie) and [cookie-parser](https://github.com/expressjs/cookie-parser). These dependencies needed to be updated together.

Updates `cookie` from 0.4.1 to 0.7.2
- [Release notes](https://github.com/jshttp/cookie/releases)
- [Commits](https://github.com/jshttp/cookie/compare/v0.4.1...v0.7.2)

Updates `cookie-parser` from 1.4.6 to 1.4.7
- [Release notes](https://github.com/expressjs/cookie-parser/releases)
- [Changelog](https://github.com/expressjs/cookie-parser/blob/master/HISTORY.md)
- [Commits](https://github.com/expressjs/cookie-parser/compare/1.4.6...1.4.7)

---
updated-dependencies:
- dependency-name: cookie
  dependency-type: indirect
- dependency-name: cookie-parser
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-05 16:07:43 -08:00
Owncast
5ca7249388 Bundle embedded web app 2024-11-05 13:19:38 +00:00
renovate[bot]
50a4224224 fix(deps): update dependency react-hotkeys-hook to v4.6.1 2024-11-05 11:18:07 +00:00
Owncast
ac009f574e Bundle embedded web app 2024-11-05 04:06:00 +00:00
renovate[bot]
b9d8fbffe2 chore(deps): update dependency sass to v1.80.6 2024-11-05 00:20:32 +00:00
Owncast
40b97e6bc1 Bundle embedded web app 2024-11-04 18:14:16 +00:00
renovate[bot]
134093b9d2 chore(deps): update dependency eslint-plugin-storybook to v0.10.2 2024-11-04 16:05:27 +00:00
Owncast
d123a2c020 Bundle embedded web app 2024-11-04 12:45:50 +00:00
renovate[bot]
fbd501f57e chore(deps): lock file maintenance 2024-11-04 12:40:56 +00:00
Owncast
ed7bd001ee Bundle embedded web app 2024-11-04 03:13:29 +00:00
renovate[bot]
83b3a0da5e chore(deps): update dependency @storybook/addon-styling-webpack to v1.0.1 2024-11-04 01:21:15 +00:00
Owncast
e04e079243 Bundle embedded web app 2024-11-04 01:19:34 +00:00
renovate[bot]
d4fd75b9c7 chore(deps): update dependency knip to v5.36.0 2024-11-03 21:52:49 +00:00
Owncast
6fbb3b9565 Bundle embedded web app 2024-11-03 21:51:32 +00:00
renovate[bot]
42005608fa chore(deps): update dependency @types/node to v20.17.5 2024-11-03 18:37:38 +00:00
Owncast
538ebaf7bb Bundle embedded web app 2024-11-03 16:22:21 +00:00
renovate[bot]
2be0aebd6d chore(deps): update storybook monorepo to v8.4.0 2024-11-03 13:37:37 +00:00
Owncast
fdaf25d92e Bundle embedded web app 2024-11-03 09:54:53 +00:00
renovate[bot]
5fc4e56bf9 chore(deps): update dependency @types/node to v20.17.4 2024-11-03 07:52:34 +00:00
Owncast
392e437b9a Bundle embedded web app 2024-11-02 19:28:48 +00:00
renovate[bot]
3066d6b82e chore(deps): update dependency mermaid to v11.4.0 2024-11-02 16:46:03 +00:00
Owncast
2f941be5c0 Bundle embedded web app 2024-11-02 16:44:41 +00:00
renovate[bot]
ff058ca777 chore(deps): update dependency knip to v5.34.4 2024-11-02 13:57:16 +00:00
Owncast
3f321d3a1c Bundle embedded web app 2024-11-02 09:46:18 +00:00
renovate[bot]
490c38bd31 fix(deps): update workbox monorepo to v7.3.0 2024-11-02 07:41:14 +00:00
Owncast
019cdb169c Bundle embedded web app 2024-11-02 07:40:21 +00:00
renovate[bot]
9d0c7c1edb chore(deps): update dependency chromatic to v11.16.3 2024-11-02 05:09:06 +00:00
Owncast
88a96f0eae Bundle embedded web app 2024-11-02 05:07:54 +00:00
renovate[bot]
b7d8a5ea99 chore(deps): update dependency sass to v1.80.5 2024-11-02 01:35:53 +00:00
Owncast
5d8c6348bc Bundle embedded web app 2024-11-02 01:34:40 +00:00
renovate[bot]
a934b89a53 chore(deps): update dependency knip to v5.34.3 2024-11-01 22:05:10 +00:00
Owncast
9ccf19eb64 Bundle embedded web app 2024-11-01 22:03:57 +00:00
renovate[bot]
de3eac2b2f chore(deps): update dependency @types/node to v20.17.3 2024-11-01 19:23:53 +00:00
Owncast
76abc0bbfb Bundle embedded web app 2024-11-01 19:22:57 +00:00
renovate[bot]
3bb2ba18e5 chore(deps): update typescript-eslint monorepo to v8.12.2 2024-11-01 16:18:21 +00:00
Owncast
e71144a68d Bundle embedded web app 2024-11-01 03:07:33 +00:00
renovate[bot]
7a367a933b chore(deps): update typescript-eslint monorepo to v8.12.1 2024-11-01 01:12:00 +00:00
Owncast
c1cb96fa57 Bundle embedded web app 2024-10-31 22:35:12 +00:00
renovate[bot]
28c6947a43 chore(deps): update dependency @types/node to v20.17.2 2024-10-31 20:48:45 +00:00
Owncast
00b9dec218 Bundle embedded web app 2024-10-31 05:20:24 +00:00
nekojanai
8e89dfb345 fix: username wrapping (#3975) 2024-10-30 22:13:28 -07:00
renovate[bot]
ed21911288 chore(deps): update peter-evans/create-or-update-comment digest to 6f2ce0e 2024-10-30 08:31:18 +00:00
Owncast
d9aeb32852 Bundle embedded web app 2024-10-29 06:34:21 +00:00
renovate[bot]
3262d3b875 chore(deps): update dependency eslint-plugin-jsx-a11y to v6.10.2 2024-10-29 05:04:08 +00:00
Owncast
af88ee386b Bundle embedded web app 2024-10-29 05:03:00 +00:00
dependabot[bot]
50f39284ce Bump elliptic from 6.5.7 to 6.6.0 in /web (#3990)
Bumps [elliptic](https://github.com/indutny/elliptic) from 6.5.7 to 6.6.0.
- [Commits](https://github.com/indutny/elliptic/compare/v6.5.7...v6.6.0)

---
updated-dependencies:
- dependency-name: elliptic
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-28 21:58:03 -07:00
Owncast
9c36e196b7 Bundle embedded web app 2024-10-29 02:01:49 +00:00
renovate[bot]
274c85ac2b chore(deps): lock file maintenance 2024-10-29 01:56:42 +00:00
Owncast
5eaa06cf15 Bundle embedded web app 2024-10-28 21:46:57 +00:00
renovate[bot]
86c20e0a39 chore(deps): update dependency chromatic to v11.16.1 2024-10-28 18:52:58 +00:00
Owncast
90a8ebbfa4 Bundle embedded web app 2024-10-28 18:50:58 +00:00
renovate[bot]
e995836ee4 chore(deps): update dependency @babel/core to v7.26.0 2024-10-28 15:58:07 +00:00
444 changed files with 63900 additions and 53040 deletions

View File

@@ -18,6 +18,7 @@ jobs:
if: github.event_name == 'pull_request'
with:
repository: ${{ github.event.pull_request.head.repo.full_name }}
ref: ${{ github.event.pull_request.head.ref }}
- name: Check out repository code
uses: actions/checkout@v4

View File

@@ -11,7 +11,7 @@ jobs:
issues: write
steps:
- name: Add comment
uses: peter-evans/create-or-update-comment@d5aa8cd1ea450824d5ae23e44f55efc071b98b44
uses: peter-evans/create-or-update-comment@54ad810bfed7d493f7413f5c35e292d18d217464
with:
issue-number: ${{ github.event.issue.number }}
body: |

View File

@@ -23,6 +23,7 @@ jobs:
if: github.event_name == 'pull_request'
with:
repository: ${{ github.event.pull_request.head.repo.full_name }}
ref: ${{ github.event.pull_request.head.ref }}
- name: Check out repository code
uses: actions/checkout@v4

View File

@@ -24,6 +24,7 @@ jobs:
if: github.event_name == 'pull_request'
with:
repository: ${{ github.event.pull_request.head.repo.full_name }}
ref: ${{ github.event.pull_request.head.ref }}
- name: Check out repository code
uses: actions/checkout@v4

View File

@@ -3,7 +3,12 @@ on:
push:
branches:
- develop
paths: ['web/stories/**', 'web/components/**', 'web/.storybook/**'] # Trigger the action only when files change in the folders defined here
paths: [
'web/stories/**',
'web/components/**',
'web/.storybook/**',
'web/i18n/**',
] # Trigger the action only when files change in the folders defined here
jobs:
build-and-deploy:
@@ -16,6 +21,7 @@ jobs:
if: github.event_name == 'pull_request'
with:
repository: ${{ github.event.pull_request.head.repo.full_name }}
ref: ${{ github.event.pull_request.head.ref }}
- name: Check out repository code
uses: actions/checkout@v4
@@ -43,6 +49,7 @@ jobs:
npm run build-storybook -- -o ../docs/components
- name: Commit changes
if: github.repository == 'owncast/owncast'
uses: EndBug/add-and-commit@v9
with:
author_name: Owncast
@@ -54,6 +61,7 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Dispatch event to web site
if: github.repository == 'owncast/owncast'
uses: peter-evans/repository-dispatch@v3
with:
token: ${{ secrets.BUNDLE_STORYBOOK_OWNCAST_ONLINE }}

View File

@@ -7,7 +7,7 @@ on:
push:
paths:
- web/**
pull_request_target:
pull_request:
paths:
- web/**
@@ -16,7 +16,6 @@ jobs:
chromatic-deployment:
# Operating System
runs-on: ubuntu-latest
if: github.repository == 'owncast/owncast'
defaults:
run:
@@ -28,15 +27,16 @@ jobs:
with:
concurrent_skipping: 'same_content_newer'
- name: Check out pull request code
uses: actions/checkout@v4
if: github.event_name == 'pull_request' || github.event_name == 'pull_request_target'
with:
repository: ${{ github.event.pull_request.head.repo.full_name }}
- name: Check out repository code
uses: actions/checkout@v4
- name: Check out repository code (Push)
if: github.event_name == 'push'
uses: actions/checkout@v4
- name: Check out pull request code (PR)
if: github.event_name == 'pull_request'
uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.ref }}
repository: ${{ github.event.pull_request.head.repo.full_name }}
- name: Get changed files
id: changed-files-yaml

View File

@@ -41,6 +41,7 @@ jobs:
if: github.event_name == 'pull_request'
with:
repository: ${{ github.event.pull_request.head.repo.full_name }}
ref: ${{ github.event.pull_request.head.ref }}
- name: Check out repository code
uses: actions/checkout@v4

View File

@@ -24,6 +24,7 @@ jobs:
if: github.event_name == 'pull_request'
with:
repository: ${{ github.event.pull_request.head.repo.full_name }}
ref: ${{ github.event.pull_request.head.ref }}
- name: Check out repository code
uses: actions/checkout@v4

View File

@@ -42,10 +42,11 @@ jobs:
if: github.event_name == 'pull_request'
with:
repository: ${{ github.event.pull_request.head.repo.full_name }}
ref: ${{ github.event.pull_request.head.ref }}
- name: Check out repository code
uses: actions/checkout@v4
if: github.event_name == 'push'
if: github.event_name == 'push' || github.event_name == 'schedule'
- name: Build and push
if: ${{ github.event_name == 'schedule' && env.GH_CR_PAT != null }}

View File

@@ -21,6 +21,7 @@ jobs:
if: github.event_name == 'pull_request'
with:
repository: ${{ github.event.pull_request.head.repo.full_name }}
ref: ${{ github.event.pull_request.head.ref }}
- name: Check out repository code
uses: actions/checkout@v4

View File

@@ -14,6 +14,7 @@ jobs:
if: github.event_name == 'pull_request'
with:
repository: ${{ github.event.pull_request.head.repo.full_name }}
ref: ${{ github.event.pull_request.head.ref }}
- name: Check out repository code
uses: actions/checkout@v4

View File

@@ -30,6 +30,7 @@ jobs:
if: github.event_name == 'pull_request'
with:
repository: ${{ github.event.pull_request.head.repo.full_name }}
ref: ${{ github.event.pull_request.head.ref }}
- name: Check out repository code
uses: actions/checkout@v4

View File

@@ -121,6 +121,7 @@ jobs:
if: github.event_name == 'pull_request'
with:
repository: ${{ github.event.pull_request.head.repo.full_name }}
ref: ${{ github.event.pull_request.head.ref }}
- name: Check out repository code
uses: actions/checkout@v4
@@ -191,6 +192,7 @@ jobs:
if: github.event_name == 'pull_request'
with:
repository: ${{ github.event.pull_request.head.repo.full_name }}
ref: ${{ github.event.pull_request.head.ref }}
- name: Check out repository code
uses: actions/checkout@v4

View File

@@ -1,67 +0,0 @@
name: Take nightly screenshots
on:
schedule:
- cron: '0 4 * * *'
env:
BROWSERSTACK_KEY: ${{ secrets.BROWSERSTACK_KEY }}
BROWSERSTACK_PASSWORD: ${{ secrets.BROWSERSTACK_PASSWORD }}
BROWSERSTACK_USERNAME: ${{ secrets.BROWSERSTACK_USERNAME }}
TEST_URL: http://localhost:8080
jobs:
Screenshots:
runs-on: ubuntu-latest
steps:
- name: Check out pull request code
uses: actions/checkout@v4
if: github.event_name == 'pull_request'
with:
repository: ${{ github.event.pull_request.head.repo.full_name }}
- name: Check out repository code
uses: actions/checkout@v4
if: github.event_name == 'push'
- uses: actions/setup-go@v5
with:
go-version: '1.22'
cache: true
- name: Cache node modules
uses: actions/cache@v4
env:
cache-name: cache-node-modules-screenshots
with:
path: ~/.npm
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('test/automated/screenshots/package-lock.json') }}
restore-keys: |
${{ runner.os }}-build-${{ env.cache-name }}-
${{ runner.os }}-build-
${{ runner.os }}-
- name: Automate screenshots
uses: nick-fields/retry@v3
with:
timeout_minutes: 10
max_attempts: 4
command: cd test/automated/screenshots && ./run.sh
- name: Commit changes
uses: EndBug/add-and-commit@v9
with:
author_name: Owncast
author_email: owncast@owncast.online
message: 'Commit screenshots'
add: '*.png'
pull: '--rebase --autostash'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Dispatch event to web site
uses: peter-evans/repository-dispatch@v3
with:
token: ${{ secrets.BUNDLE_STORYBOOK_OWNCAST_ONLINE }}
repository: owncast/owncast.github.io
event-type: bundle-components-library

View File

@@ -14,6 +14,7 @@ jobs:
days-before-issue-stale: 60
days-before-issue-close: 67
exempt-issue-labels: backlog,long-lived,bot
exempt-all-issue-milestones: true
stale-issue-message: >
This issue has been automatically marked as stale because it has not had
recent activity. It will be closed if no further activity occurs. If this
@@ -33,6 +34,8 @@ jobs:
days-before-pr-stale: 30
days-before-pr-close: 37
exempt-pr-labels: backlog,long-lived,bot
exempt-all-pr-milestones: true
stale-pr-message: >
This pull request has not had any activity in 30 days. If it has been abandoned
no future actions are necessary, it will be automatically closed. If this is a PR

62
.github/workflows/translations.yml vendored Normal file
View File

@@ -0,0 +1,62 @@
name: Translation job
on:
schedule:
# Run the workflow every hour
- cron: '0 * * * *'
push:
paths:
- 'web/i18n/en/translation.json'
- 'web/**/*.tsx'
- 'web/**/*.js'
- 'crowdin.yml'
- '.github/workflows/translations.yml'
- 'web/i18next-parser.config.mjs'
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs:
generate-translations:
defaults:
run:
working-directory: ./web
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install dependencies
if: ${{ github.actor != 'renovate[bot]' && github.actor != 'renovate' }}
run: npm install
- name: Generate translation files
run: npm run translate
- name: Crowdin upload sources/download translations
uses: crowdin/github-action@v1
with:
upload_sources: true
download_translations: true
create_pull_request: true
pull_request_title: 'New Translations'
localization_branch_name: translations
pull_request_base_branch_name: 'develop'
commit_message: 'Updated translations'
config: crowdin.yml
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
CROWDIN_PROJECT_ID: ${{ secrets.CROWDIN_PROJECT_ID }}
CROWDIN_PERSONAL_TOKEN: ${{ secrets.CROWDIN_PERSONAL_TOKEN }}
# - name: Commit changes
# uses: EndBug/add-and-commit@v9
# with:
# author_name: Owncast
# author_email: owncast@owncast.online
# message: 'Commit updated translations'
# add: 'web/i18n/**'
# env:
# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

1
.gitignore vendored
View File

@@ -4,6 +4,7 @@
*.dll
*.so
*.dylib
.DS_Store
# Test binary, built with `go test -c`
*.test

View File

@@ -22,7 +22,7 @@ ENV NAME=${NAME}
RUN CGO_ENABLED=1 GOOS=linux go build -a -installsuffix cgo -ldflags "-extldflags \"-static\" -s -w -X github.com/owncast/owncast/config.GitCommit=$GIT_COMMIT -X github.com/owncast/owncast/config.VersionNumber=$VERSION -X github.com/owncast/owncast/config.BuildPlatform=$NAME" -o owncast .
# Create the image by copying the result of the build into a new alpine image
FROM alpine:3.20.3
FROM alpine:3.21.3
RUN apk update && apk add --no-cache ffmpeg ffmpeg-libs ca-certificates && update-ca-certificates
RUN addgroup -g 101 -S owncast && adduser -u 101 -S owncast -G owncast

View File

@@ -1,6 +1,6 @@
VERSION --new-platform 0.6
FROM --platform=linux/amd64 alpine:3.20.3
FROM --platform=linux/amd64 alpine:3.21.3
ARG version=develop
WORKDIR /build
@@ -119,7 +119,7 @@ docker:
# in as space separated strings using the full account/repo:tag format.
# https://github.com/earthly/earthly/blob/aea38448fa9c0064b1b70d61be717ae740689fb9/docs/earthfile/earthfile.md#assigning-multiple-image-names
ARG TARGETPLATFORM
FROM --platform=$TARGETPLATFORM alpine:3.20.3
FROM --platform=$TARGETPLATFORM alpine:3.21.3
RUN apk update && apk add --no-cache ffmpeg ffmpeg-libs ca-certificates unzip && update-ca-certificates
RUN addgroup -g 101 -S owncast && adduser -u 101 -S owncast -G owncast
WORKDIR /app

View File

@@ -1,11 +1,14 @@
package activitypub
import (
"math"
"github.com/owncast/owncast/activitypub/crypto"
"github.com/owncast/owncast/activitypub/inbox"
"github.com/owncast/owncast/activitypub/outbox"
"github.com/owncast/owncast/activitypub/persistence"
"github.com/owncast/owncast/activitypub/workerpool"
"github.com/owncast/owncast/persistence/configrepository"
"github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/models"
@@ -14,21 +17,35 @@ import (
// Start will initialize and start the federation support.
func Start(datastore *data.Datastore) {
configRepository := configrepository.Get()
persistence.Setup(datastore)
workerpool.InitOutboundWorkerPool()
outboundWorkerPoolSize := getOutboundWorkerPoolSize()
workerpool.InitOutboundWorkerPool(outboundWorkerPoolSize)
inbox.InitInboxWorkerPool()
// Generate the keys for signing federated activity if needed.
if data.GetPrivateKey() == "" {
if configRepository.GetPrivateKey() == "" {
privateKey, publicKey, err := crypto.GenerateKeys()
_ = data.SetPrivateKey(string(privateKey))
_ = data.SetPublicKey(string(publicKey))
_ = configRepository.SetPrivateKey(string(privateKey))
_ = configRepository.SetPublicKey(string(publicKey))
if err != nil {
log.Errorln("Unable to get private key", err)
}
}
}
func getOutboundWorkerPoolSize() int {
var followerCount int64
fc, err := persistence.GetFollowerCount()
if err != nil {
log.Errorln("Unable to get follower count", err)
fc = 50 // Arbitrary fallback value.
}
followerCount = int64(math.Max(float64(fc), 50))
return int(followerCount * 5)
}
// SendLive will send a "Go Live" message to followers.
func SendLive() error {
return outbox.SendLive()

View File

@@ -6,7 +6,7 @@ import (
"github.com/go-fed/activity/streams"
"github.com/go-fed/activity/streams/vocab"
"github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/persistence/configrepository"
)
// PrivacyAudience represents the audience for an activity.
@@ -87,8 +87,10 @@ func MakeActivityDirect(activity vocab.ActivityStreamsCreate, toIRI *url.URL) vo
// MakeActivityPublic sets the required properties to make this activity
// seen as public.
func MakeActivityPublic(activity vocab.ActivityStreamsCreate) vocab.ActivityStreamsCreate {
configRepository := configrepository.Get()
// TO the public if we're not treating ActivityPub as "private".
if !data.GetFederationIsPrivate() {
if !configRepository.GetFederationIsPrivate() {
public, _ := url.Parse(PUBLIC)
to := streams.NewActivityStreamsToProperty()
@@ -121,7 +123,9 @@ func MakeUpdateActivity(activityID *url.URL) vocab.ActivityStreamsUpdate {
activity.SetJSONLDId(id)
// CC the public if we're not treating ActivityPub as "private".
if !data.GetFederationIsPrivate() {
configRepository := configrepository.Get()
if !configRepository.GetFederationIsPrivate() {
public, _ := url.Parse(PUBLIC)
cc := streams.NewActivityStreamsCcProperty()
cc.AppendIRI(public)

View File

@@ -9,8 +9,8 @@ import (
"github.com/go-fed/activity/streams"
"github.com/go-fed/activity/streams/vocab"
"github.com/owncast/owncast/activitypub/crypto"
"github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/models"
"github.com/owncast/owncast/persistence/configrepository"
log "github.com/sirupsen/logrus"
)
@@ -101,11 +101,13 @@ func MakeActorPropertyWithID(idIRI *url.URL) vocab.ActivityStreamsActorProperty
// MakeServiceForAccount will create a new local actor service with the the provided username.
func MakeServiceForAccount(accountName string) vocab.ActivityStreamsService {
configRepository := configrepository.Get()
actorIRI := MakeLocalIRIForAccount(accountName)
person := streams.NewActivityStreamsService()
nameProperty := streams.NewActivityStreamsNameProperty()
nameProperty.AppendXMLSchemaString(data.GetServerName())
nameProperty.AppendXMLSchemaString(configRepository.GetServerName())
person.SetActivityStreamsName(nameProperty)
preferredUsernameProperty := streams.NewActivityStreamsPreferredUsernameProperty()
@@ -119,7 +121,7 @@ func MakeServiceForAccount(accountName string) vocab.ActivityStreamsService {
person.SetActivityStreamsInbox(inboxProp)
needsFollowApprovalProperty := streams.NewActivityStreamsManuallyApprovesFollowersProperty()
needsFollowApprovalProperty.Set(data.GetFederationIsPrivate())
needsFollowApprovalProperty.Set(configRepository.GetFederationIsPrivate())
person.SetActivityStreamsManuallyApprovesFollowers(needsFollowApprovalProperty)
outboxIRI := MakeLocalIRIForResource("/user/" + accountName + "/outbox")
@@ -152,7 +154,7 @@ func MakeServiceForAccount(accountName string) vocab.ActivityStreamsService {
publicKeyProp.AppendW3IDSecurityV1PublicKey(publicKeyType)
person.SetW3IDSecurityV1PublicKey(publicKeyProp)
if t, err := data.GetServerInitTime(); t != nil {
if t, err := configRepository.GetServerInitTime(); t != nil {
publishedDateProp := streams.NewActivityStreamsPublishedProperty()
publishedDateProp.Set(t.Time)
person.SetActivityStreamsPublished(publishedDateProp)
@@ -163,8 +165,8 @@ func MakeServiceForAccount(accountName string) vocab.ActivityStreamsService {
// Profile properties
// Avatar
uniquenessString := data.GetLogoUniquenessString()
userAvatarURLString := data.GetServerURL() + "/logo/external"
uniquenessString := configRepository.GetLogoUniquenessString()
userAvatarURLString := configRepository.GetServerURL() + "/logo/external"
userAvatarURL, err := url.Parse(userAvatarURLString)
userAvatarURL.RawQuery = "uc=" + uniquenessString
if err != nil {
@@ -195,14 +197,14 @@ func MakeServiceForAccount(accountName string) vocab.ActivityStreamsService {
// Profile bio
summaryProperty := streams.NewActivityStreamsSummaryProperty()
summaryProperty.AppendXMLSchemaString(data.GetServerSummary())
summaryProperty.AppendXMLSchemaString(configRepository.GetServerSummary())
person.SetActivityStreamsSummary(summaryProperty)
// Links
if serverURL := data.GetServerURL(); serverURL != "" {
if serverURL := configRepository.GetServerURL(); serverURL != "" {
addMetadataLinkToProfile(person, "Stream", serverURL)
}
for _, link := range data.GetSocialHandles() {
for _, link := range configRepository.GetSocialHandles() {
addMetadataLinkToProfile(person, link.Platform, link.URL)
}
@@ -220,7 +222,7 @@ func MakeServiceForAccount(accountName string) vocab.ActivityStreamsService {
// Tags
tagProp := streams.NewActivityStreamsTagProperty()
for _, tagString := range data.GetServerMetadataTags() {
for _, tagString := range configRepository.GetServerMetadataTags() {
hashtag := MakeHashtag(tagString)
tagProp.AppendTootHashtag(hashtag)
}
@@ -229,7 +231,7 @@ func MakeServiceForAccount(accountName string) vocab.ActivityStreamsService {
// Work around an issue where a single attachment will not serialize
// as an array, so add another item to the mix.
if len(data.GetSocialHandles()) == 1 {
if len(configRepository.GetSocialHandles()) == 1 {
addMetadataLinkToProfile(person, "Owncast", "https://owncast.online")
}

View File

@@ -9,6 +9,7 @@ import (
"github.com/go-fed/activity/streams"
"github.com/go-fed/activity/streams/vocab"
"github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/persistence/configrepository"
)
func makeFakeService() vocab.ActivityStreamsService {
@@ -55,9 +56,11 @@ func TestMain(m *testing.M) {
if err != nil {
panic(err)
}
data.SetupPersistence(dbFile.Name())
data.SetServerURL("https://my.cool.site.biz")
configRepository := configrepository.Get()
configRepository.SetServerURL("https://my.cool.site.biz")
m.Run()
}

View File

@@ -8,7 +8,7 @@ import (
"github.com/go-fed/activity/streams"
"github.com/go-fed/activity/streams/vocab"
"github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/persistence/configrepository"
log "github.com/sirupsen/logrus"
)
@@ -27,7 +27,9 @@ func MakeRemoteIRIForResource(resourcePath string, host string) (*url.URL, error
// MakeLocalIRIForResource will create an IRI for the local server.
func MakeLocalIRIForResource(resourcePath string) *url.URL {
host := data.GetServerURL()
configRepository := configrepository.Get()
host := configRepository.GetServerURL()
u, err := url.Parse(host)
if err != nil {
log.Errorln("unable to parse local IRI url", host, err)
@@ -41,7 +43,9 @@ func MakeLocalIRIForResource(resourcePath string) *url.URL {
// MakeLocalIRIForAccount will return a full IRI for the local server account username.
func MakeLocalIRIForAccount(account string) *url.URL {
host := data.GetServerURL()
configRepository := configrepository.Get()
host := configRepository.GetServerURL()
u, err := url.Parse(host)
if err != nil {
log.Errorln("unable to parse local IRI account server url", err)
@@ -64,7 +68,9 @@ func Serialize(obj vocab.Type) ([]byte, error) {
// MakeLocalIRIForStreamURL will return a full IRI for the local server stream url.
func MakeLocalIRIForStreamURL() *url.URL {
host := data.GetServerURL()
configRepository := configrepository.Get()
host := configRepository.GetServerURL()
u, err := url.Parse(host)
if err != nil {
log.Errorln("unable to parse local IRI stream url", err)
@@ -78,7 +84,9 @@ func MakeLocalIRIForStreamURL() *url.URL {
// MakeLocalIRIforLogo will return a full IRI for the local server logo.
func MakeLocalIRIforLogo() *url.URL {
host := data.GetServerURL()
configRepository := configrepository.Get()
host := configRepository.GetServerURL()
u, err := url.Parse(host)
if err != nil {
log.Errorln("unable to parse local IRI stream url", err)
@@ -93,7 +101,9 @@ func MakeLocalIRIforLogo() *url.URL {
// GetLogoType will return the rel value for the webfinger response and
// the default static image is of type png.
func GetLogoType() string {
imageFilename := data.GetLogoPath()
configRepository := configrepository.Get()
imageFilename := configRepository.GetLogoPath()
if imageFilename == "" {
return "image/png"
}

View File

@@ -9,12 +9,14 @@ import (
"github.com/owncast/owncast/activitypub/apmodels"
"github.com/owncast/owncast/activitypub/crypto"
"github.com/owncast/owncast/activitypub/requests"
"github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/persistence/configrepository"
)
// ActorHandler handles requests for a single actor.
func ActorHandler(w http.ResponseWriter, r *http.Request) {
if !data.GetFederationEnabled() {
configRepository := configrepository.Get()
if !configRepository.GetFederationEnabled() {
w.WriteHeader(http.StatusMethodNotAllowed)
return
}
@@ -22,7 +24,7 @@ func ActorHandler(w http.ResponseWriter, r *http.Request) {
pathComponents := strings.Split(r.URL.Path, "/")
accountName := pathComponents[3]
if _, valid := data.GetFederatedInboxMap()[accountName]; !valid {
if _, valid := configRepository.GetFederatedInboxMap()[accountName]; !valid {
// User is not valid
w.WriteHeader(http.StatusNotFound)
return

View File

@@ -16,7 +16,7 @@ import (
"github.com/owncast/owncast/activitypub/crypto"
"github.com/owncast/owncast/activitypub/persistence"
"github.com/owncast/owncast/activitypub/requests"
"github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/persistence/configrepository"
)
const (
@@ -145,7 +145,9 @@ func getFollowersPage(page string, r *http.Request) (vocab.ActivityStreamsOrdere
}
func createPageURL(r *http.Request, page *string) (*url.URL, error) {
domain := data.GetServerURL()
configRepository := configrepository.Get()
domain := configRepository.GetServerURL()
if domain == "" {
return nil, errors.New("unable to get server URL")
}

View File

@@ -7,7 +7,7 @@ import (
"github.com/owncast/owncast/activitypub/apmodels"
"github.com/owncast/owncast/activitypub/inbox"
"github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/persistence/configrepository"
log "github.com/sirupsen/logrus"
)
@@ -22,7 +22,9 @@ func InboxHandler(w http.ResponseWriter, r *http.Request) {
}
func acceptInboxRequest(w http.ResponseWriter, r *http.Request) {
if !data.GetFederationEnabled() {
configRepository := configrepository.Get()
if !configRepository.GetFederationEnabled() {
w.WriteHeader(http.StatusMethodNotAllowed)
return
}
@@ -39,7 +41,7 @@ func acceptInboxRequest(w http.ResponseWriter, r *http.Request) {
// The account this request is for must match the account name we have set
// for federation.
if forLocalAccount != data.GetFederationUsername() {
if forLocalAccount != configRepository.GetFederationUsername() {
w.WriteHeader(http.StatusNotFound)
return
}

View File

@@ -10,7 +10,7 @@ import (
"github.com/owncast/owncast/activitypub/persistence"
"github.com/owncast/owncast/activitypub/requests"
"github.com/owncast/owncast/config"
"github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/persistence/configrepository"
log "github.com/sirupsen/logrus"
)
@@ -25,12 +25,14 @@ func NodeInfoController(w http.ResponseWriter, r *http.Request) {
Links []links `json:"links"`
}
if !data.GetFederationEnabled() {
configRepository := configrepository.Get()
if !configRepository.GetFederationEnabled() {
w.WriteHeader(http.StatusMethodNotAllowed)
return
}
serverURL := data.GetServerURL()
serverURL := configRepository.GetServerURL()
if serverURL == "" {
w.WriteHeader(http.StatusNotFound)
return
@@ -89,7 +91,9 @@ func NodeInfoV2Controller(w http.ResponseWriter, r *http.Request) {
Metadata metadata `json:"metadata"`
}
if !data.GetFederationEnabled() {
configRepository := configrepository.Get()
if !configRepository.GetFederationEnabled() {
w.WriteHeader(http.StatusMethodNotAllowed)
return
}
@@ -117,7 +121,7 @@ func NodeInfoV2Controller(w http.ResponseWriter, r *http.Request) {
OpenRegistrations: false,
Protocols: []string{"activitypub"},
Metadata: metadata{
ChatEnabled: !data.GetChatDisabled(),
ChatEnabled: !configRepository.GetChatDisabled(),
},
}
@@ -163,12 +167,14 @@ func XNodeInfo2Controller(w http.ResponseWriter, r *http.Request) {
OpenRegistrations bool `json:"openRegistrations"`
}
if !data.GetFederationEnabled() {
configRepository := configrepository.Get()
if !configRepository.GetFederationEnabled() {
w.WriteHeader(http.StatusMethodNotAllowed)
return
}
serverURL := data.GetServerURL()
serverURL := configRepository.GetServerURL()
if serverURL == "" {
w.WriteHeader(http.StatusNotFound)
return
@@ -178,7 +184,7 @@ func XNodeInfo2Controller(w http.ResponseWriter, r *http.Request) {
res := &response{
Organization: Organization{
Name: data.GetServerName(),
Name: configRepository.GetServerName(),
Contact: serverURL,
},
Server: Server{
@@ -232,12 +238,14 @@ func InstanceV1Controller(w http.ResponseWriter, r *http.Request) {
InvitesEnabled bool `json:"invites_enabled"`
}
if !data.GetFederationEnabled() {
configRepository := configrepository.Get()
if !configRepository.GetFederationEnabled() {
w.WriteHeader(http.StatusMethodNotAllowed)
return
}
serverURL := data.GetServerURL()
serverURL := configRepository.GetServerURL()
if serverURL == "" {
w.WriteHeader(http.StatusNotFound)
return
@@ -254,9 +262,9 @@ func InstanceV1Controller(w http.ResponseWriter, r *http.Request) {
res := response{
URI: serverURL,
Title: data.GetServerName(),
ShortDescription: data.GetServerSummary(),
Description: data.GetServerSummary(),
Title: configRepository.GetServerName(),
ShortDescription: configRepository.GetServerSummary(),
Description: configRepository.GetServerSummary(),
Version: config.GetReleaseString(),
Stats: Stats{
UserCount: 1,
@@ -275,7 +283,9 @@ func InstanceV1Controller(w http.ResponseWriter, r *http.Request) {
}
func writeResponse(payload interface{}, w http.ResponseWriter) error {
accountName := data.GetDefaultFederationUsername()
configRepository := configrepository.Get()
accountName := configRepository.GetDefaultFederationUsername()
actorIRI := apmodels.MakeLocalIRIForAccount(accountName)
publicKey := crypto.GetPublicKey(actorIRI)
@@ -284,13 +294,15 @@ func writeResponse(payload interface{}, w http.ResponseWriter) error {
// HostMetaController points to webfinger.
func HostMetaController(w http.ResponseWriter, r *http.Request) {
if !data.GetFederationEnabled() {
configRepository := configrepository.Get()
if !configRepository.GetFederationEnabled() {
w.WriteHeader(http.StatusMethodNotAllowed)
log.Debugln("host meta request rejected! Federation is not enabled")
return
}
serverURL := data.GetServerURL()
serverURL := configRepository.GetServerURL()
if serverURL == "" {
w.WriteHeader(http.StatusNotFound)
return

View File

@@ -8,31 +8,33 @@ import (
"github.com/owncast/owncast/activitypub/crypto"
"github.com/owncast/owncast/activitypub/persistence"
"github.com/owncast/owncast/activitypub/requests"
"github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/persistence/configrepository"
log "github.com/sirupsen/logrus"
)
// ObjectHandler handles requests for a single federated ActivityPub object.
func ObjectHandler(w http.ResponseWriter, r *http.Request) {
if !data.GetFederationEnabled() {
configRepository := configrepository.Get()
if !configRepository.GetFederationEnabled() {
w.WriteHeader(http.StatusMethodNotAllowed)
return
}
// If private federation mode is enabled do not allow access to objects.
if data.GetFederationIsPrivate() {
if configRepository.GetFederationIsPrivate() {
w.WriteHeader(http.StatusNotFound)
return
}
iri := strings.Join([]string{strings.TrimSuffix(data.GetServerURL(), "/"), r.URL.Path}, "")
iri := strings.Join([]string{strings.TrimSuffix(configRepository.GetServerURL(), "/"), r.URL.Path}, "")
object, _, _, err := persistence.GetObjectByIRI(iri)
if err != nil {
w.WriteHeader(http.StatusNotFound)
return
}
accountName := data.GetDefaultFederationUsername()
accountName := configRepository.GetDefaultFederationUsername()
actorIRI := apmodels.MakeLocalIRIForAccount(accountName)
publicKey := crypto.GetPublicKey(actorIRI)

View File

@@ -6,20 +6,22 @@ import (
"strings"
"github.com/owncast/owncast/activitypub/apmodels"
"github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/persistence/configrepository"
"github.com/owncast/owncast/utils"
log "github.com/sirupsen/logrus"
)
// WebfingerHandler will handle webfinger lookup requests.
func WebfingerHandler(w http.ResponseWriter, r *http.Request) {
if !data.GetFederationEnabled() {
configRepository := configrepository.Get()
if !configRepository.GetFederationEnabled() {
w.WriteHeader(http.StatusMethodNotAllowed)
log.Debugln("webfinger request rejected! Federation is not enabled")
return
}
instanceHostURL := data.GetServerURL()
instanceHostURL := configRepository.GetServerURL()
if instanceHostURL == "" {
w.WriteHeader(http.StatusNotFound)
log.Warnln("webfinger request rejected! Federation is enabled but server URL is empty.")
@@ -29,7 +31,7 @@ func WebfingerHandler(w http.ResponseWriter, r *http.Request) {
instanceHostString := utils.GetHostnameFromURLString(instanceHostURL)
if instanceHostString == "" {
w.WriteHeader(http.StatusNotFound)
log.Warnln("webfinger request rejected! Federation is enabled but server URL is not set properly. data.GetServerURL(): " + data.GetServerURL())
log.Warnln("webfinger request rejected! Federation is enabled but server URL is not set properly. data.GetServerURL(): " + configRepository.GetServerURL())
return
}
@@ -51,7 +53,7 @@ func WebfingerHandler(w http.ResponseWriter, r *http.Request) {
host := userComponents[1]
user := userComponents[0]
if _, valid := data.GetFederatedInboxMap()[user]; !valid {
if _, valid := configRepository.GetFederatedInboxMap()[user]; !valid {
w.WriteHeader(http.StatusNotFound)
log.Debugln("webfinger request rejected! Invalid user: " + user)
return

View File

@@ -8,13 +8,15 @@ import (
"errors"
"net/url"
"github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/persistence/configrepository"
log "github.com/sirupsen/logrus"
)
// GetPublicKey will return the public key for the provided actor.
func GetPublicKey(actorIRI *url.URL) PublicKey {
key := data.GetPublicKey()
configRepository := configrepository.Get()
key := configRepository.GetPublicKey()
idURL, err := url.Parse(actorIRI.String() + "#main-key")
if err != nil {
log.Errorln("unable to parse actor iri string", idURL, err)
@@ -29,7 +31,9 @@ func GetPublicKey(actorIRI *url.URL) PublicKey {
// GetPrivateKey will return the internal server private key.
func GetPrivateKey() *rsa.PrivateKey {
key := data.GetPrivateKey()
configRepository := configrepository.Get()
key := configRepository.GetPrivateKey()
block, _ := pem.Decode([]byte(key))
if block == nil {

View File

@@ -7,17 +7,19 @@ import (
"github.com/owncast/owncast/activitypub/resolvers"
"github.com/owncast/owncast/core/chat"
"github.com/owncast/owncast/core/chat/events"
"github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/persistence/configrepository"
)
func handleEngagementActivity(eventType events.EventType, isLiveNotification bool, actorReference vocab.ActivityStreamsActorProperty, action string) error {
configRepository := configrepository.Get()
// Do nothing if displaying engagement actions has been turned off.
if !data.GetFederationShowEngagement() {
if !configRepository.GetFederationShowEngagement() {
return nil
}
// Do nothing if chat is disabled
if data.GetChatDisabled() {
if configRepository.GetChatDisabled() {
return nil
}
@@ -36,11 +38,11 @@ func handleEngagementActivity(eventType events.EventType, isLiveNotification boo
if isLiveNotification && action == events.FediverseEngagementLike {
suffix = "liked that this stream went live."
} else if action == events.FediverseEngagementLike {
suffix = fmt.Sprintf("liked a post from %s.", data.GetServerName())
suffix = fmt.Sprintf("liked a post from %s.", configRepository.GetServerName())
} else if isLiveNotification && action == events.FediverseEngagementRepost {
suffix = "shared this stream with their followers."
} else if action == events.FediverseEngagementRepost {
suffix = fmt.Sprintf("shared a post from %s.", data.GetServerName())
suffix = fmt.Sprintf("shared a post from %s.", configRepository.GetServerName())
} else if action == events.FediverseEngagementFollow {
suffix = "followed this stream."
} else {

View File

@@ -10,13 +10,15 @@ import (
"github.com/owncast/owncast/activitypub/requests"
"github.com/owncast/owncast/activitypub/resolvers"
"github.com/owncast/owncast/core/chat/events"
"github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/persistence/configrepository"
"github.com/pkg/errors"
log "github.com/sirupsen/logrus"
)
func handleFollowInboxRequest(c context.Context, activity vocab.ActivityStreamsFollow) error {
configRepository := configrepository.Get()
follow, err := resolvers.MakeFollowRequest(c, activity)
if err != nil {
log.Errorln("unable to create follow inbox request", err)
@@ -27,7 +29,7 @@ func handleFollowInboxRequest(c context.Context, activity vocab.ActivityStreamsF
return fmt.Errorf("unable to handle request")
}
approved := !data.GetFederationIsPrivate()
approved := !configRepository.GetFederationIsPrivate()
followRequest := *follow
@@ -36,7 +38,7 @@ func handleFollowInboxRequest(c context.Context, activity vocab.ActivityStreamsF
return err
}
localAccountName := data.GetDefaultFederationUsername()
localAccountName := configRepository.GetDefaultFederationUsername()
if approved {
if err := requests.SendFollowAccept(follow.Inbox, activity, localAccountName); err != nil {
@@ -45,7 +47,7 @@ func handleFollowInboxRequest(c context.Context, activity vocab.ActivityStreamsF
}
}
// Save as an accepted activity
// Save as an activity
actorReference := activity.GetActivityStreamsActor()
object := activity.GetActivityStreamsObject()
objectIRI := object.At(0).GetIRI().String()

View File

@@ -15,7 +15,7 @@ import (
"github.com/owncast/owncast/activitypub/apmodels"
"github.com/owncast/owncast/activitypub/persistence"
"github.com/owncast/owncast/activitypub/resolvers"
"github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/persistence/configrepository"
log "github.com/sirupsen/logrus"
)
@@ -131,7 +131,9 @@ func Verify(request *http.Request) (bool, error) {
}
func isBlockedDomain(domain string) bool {
blockedDomains := data.GetBlockedFederatedDomains()
configRepository := configrepository.Get()
blockedDomains := configRepository.GetBlockedFederatedDomains()
for _, blockedDomain := range blockedDomains {
if strings.Contains(domain, blockedDomain) {

View File

@@ -9,6 +9,7 @@ import (
"github.com/owncast/owncast/activitypub/apmodels"
"github.com/owncast/owncast/activitypub/persistence"
"github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/persistence/configrepository"
)
func makeFakePerson() vocab.ActivityStreamsPerson {
@@ -49,21 +50,24 @@ func makeFakePerson() vocab.ActivityStreamsPerson {
func TestMain(m *testing.M) {
data.SetupPersistence(":memory:")
data.SetServerURL("https://my.cool.site.biz")
configRepository := configrepository.Get()
configRepository.SetServerURL("https://my.cool.site.biz")
persistence.Setup(data.GetDatastore())
m.Run()
}
func TestBlockedDomains(t *testing.T) {
configRepository := configrepository.Get()
person := makeFakePerson()
data.SetBlockedFederatedDomains([]string{"freedom.eagle", "guns.life"})
configRepository.SetBlockedFederatedDomains([]string{"freedom.eagle", "guns.life"})
if len(data.GetBlockedFederatedDomains()) != 2 {
if len(configRepository.GetBlockedFederatedDomains()) != 2 {
t.Error("Blocked federated domains is not set correctly")
}
for _, domain := range data.GetBlockedFederatedDomains() {
for _, domain := range configRepository.GetBlockedFederatedDomains() {
if domain == person.GetJSONLDId().GetIRI().Host {
return
}

View File

@@ -16,10 +16,10 @@ import (
"github.com/owncast/owncast/activitypub/resolvers"
"github.com/owncast/owncast/activitypub/webfinger"
"github.com/owncast/owncast/activitypub/workerpool"
"github.com/owncast/owncast/persistence/configrepository"
"github.com/pkg/errors"
"github.com/owncast/owncast/config"
"github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/utils"
log "github.com/sirupsen/logrus"
"github.com/teris-io/shortid"
@@ -27,7 +27,9 @@ import (
// SendLive will send all followers the message saying you started a live stream.
func SendLive() error {
textContent := data.GetFederationGoLiveMessage()
configRepository := configrepository.Get()
textContent := configRepository.GetFederationGoLiveMessage()
// If the message is empty then do not send it.
if textContent == "" {
@@ -38,7 +40,7 @@ func SendLive() error {
reg := regexp.MustCompile("[^a-zA-Z0-9]+")
tagProp := streams.NewActivityStreamsTagProperty()
for _, tagString := range data.GetServerMetadataTags() {
for _, tagString := range configRepository.GetServerMetadataTags() {
tagWithoutSpecialCharacters := reg.ReplaceAllString(tagString, "")
hashtag := apmodels.MakeHashtag(tagWithoutSpecialCharacters)
tagProp.AppendTootHashtag(hashtag)
@@ -57,15 +59,15 @@ func SendLive() error {
tagsString := strings.Join(tagStrings, " ")
var streamTitle string
if title := data.GetStreamTitle(); title != "" {
if title := configRepository.GetStreamTitle(); title != "" {
streamTitle = fmt.Sprintf("<p>%s</p>", title)
}
textContent = fmt.Sprintf("<p>%s</p>%s<p>%s</p><p><a href=\"%s\">%s</a></p>", textContent, streamTitle, tagsString, data.GetServerURL(), data.GetServerURL())
textContent = fmt.Sprintf("<p>%s</p>%s<p>%s</p><p><a href=\"%s\">%s</a></p>", textContent, streamTitle, tagsString, configRepository.GetServerURL(), configRepository.GetServerURL())
activity, _, note, noteID := createBaseOutboundMessage(textContent)
// To the public if we're not treating ActivityPub as "private".
if !data.GetFederationIsPrivate() {
if !configRepository.GetFederationIsPrivate() {
note = apmodels.MakeNotePublic(note)
activity = apmodels.MakeActivityPublic(activity)
}
@@ -73,7 +75,7 @@ func SendLive() error {
note.SetActivityStreamsTag(tagProp)
// Attach an image along with the Federated message.
previewURL, err := url.Parse(data.GetServerURL())
previewURL, err := url.Parse(configRepository.GetServerURL())
if err == nil {
var imageToAttach string
var mediaType string
@@ -94,7 +96,7 @@ func SendLive() error {
}
}
if data.GetNSFW() {
if configRepository.GetNSFW() {
// Mark content as sensitive.
sensitive := streams.NewActivityStreamsSensitiveProperty()
sensitive.AppendXMLSchemaBoolean(true)
@@ -151,6 +153,8 @@ func SendDirectMessageToAccount(textContent, account string) error {
// SendPublicMessage will send a public message to all followers.
func SendPublicMessage(textContent string) error {
configRepository := configrepository.Get()
originalContent := textContent
textContent = utils.RenderSimpleMarkdown(textContent)
@@ -173,7 +177,7 @@ func SendPublicMessage(textContent string) error {
activity, _, note, noteID := createBaseOutboundMessage(textContent)
note.SetActivityStreamsTag(tagProp)
if !data.GetFederationIsPrivate() {
if !configRepository.GetFederationIsPrivate() {
note = apmodels.MakeNotePublic(note)
activity = apmodels.MakeActivityPublic(activity)
}
@@ -197,7 +201,8 @@ func SendPublicMessage(textContent string) error {
// nolint: unparam
func createBaseOutboundMessage(textContent string) (vocab.ActivityStreamsCreate, string, vocab.ActivityStreamsNote, string) {
localActor := apmodels.MakeLocalIRIForAccount(data.GetDefaultFederationUsername())
configRepository := configrepository.Get()
localActor := apmodels.MakeLocalIRIForAccount(configRepository.GetDefaultFederationUsername())
noteID := shortid.MustGenerate()
noteIRI := apmodels.MakeLocalIRIForResource(noteID)
id := shortid.MustGenerate()
@@ -218,7 +223,8 @@ func getHashtagLinkHTMLFromTagString(baseHashtag string) string {
// SendToFollowers will send an arbitrary payload to all follower inboxes.
func SendToFollowers(payload []byte) error {
localActor := apmodels.MakeLocalIRIForAccount(data.GetDefaultFederationUsername())
configRepository := configrepository.Get()
localActor := apmodels.MakeLocalIRIForAccount(configRepository.GetDefaultFederationUsername())
followers, _, err := persistence.GetFederationFollowers(-1, 0)
if err != nil {
@@ -241,7 +247,8 @@ func SendToFollowers(payload []byte) error {
// SendToUser will send a payload to a single specific inbox.
func SendToUser(inbox *url.URL, payload []byte) error {
localActor := apmodels.MakeLocalIRIForAccount(data.GetDefaultFederationUsername())
configRepository := configrepository.Get()
localActor := apmodels.MakeLocalIRIForAccount(configRepository.GetDefaultFederationUsername())
req, err := requests.CreateSignedRequest(payload, inbox, localActor)
if err != nil {
@@ -255,8 +262,10 @@ func SendToUser(inbox *url.URL, payload []byte) error {
// UpdateFollowersWithAccountUpdates will send an update to all followers alerting of a profile update.
func UpdateFollowersWithAccountUpdates() error {
configRepository := configrepository.Get()
// Don't do anything if federation is disabled.
if !data.GetFederationEnabled() {
if !configRepository.GetFederationEnabled() {
return nil
}
@@ -265,7 +274,7 @@ func UpdateFollowersWithAccountUpdates() error {
activity := apmodels.MakeUpdateActivity(objectID)
actor := streams.NewActivityStreamsPerson()
actorID := apmodels.MakeLocalIRIForAccount(data.GetDefaultFederationUsername())
actorID := apmodels.MakeLocalIRIForAccount(configRepository.GetDefaultFederationUsername())
actorIDProperty := streams.NewJSONLDIdProperty()
actorIDProperty.Set(actorID)
actor.SetJSONLDId(actorIDProperty)

View File

@@ -3,6 +3,7 @@ package persistence
import (
"context"
"database/sql"
"encoding/json"
"fmt"
"net/url"
"time"
@@ -74,6 +75,27 @@ func GetFollower(iri string) (*apmodels.ActivityPubActor, error) {
return nil, errors.Wrap(err, "error parsing acting inbox")
}
requestObjectBytes := result.RequestObject
var followRequestObject vocab.ActivityStreamsFollow
resolver, err := streams.NewJSONResolver(func(c context.Context, followObject vocab.ActivityStreamsFollow) error {
followRequestObject = followObject
return nil
})
if err != nil {
return nil, errors.Wrap(err, "error creating JSON resolver")
}
jsonMap := make(map[string]interface{})
err = json.Unmarshal(requestObjectBytes, &jsonMap)
if err != nil {
return nil, errors.Wrap(err, "error unmarshaling follow request object")
}
err = resolver.Resolve(context.Background(), jsonMap)
if err != nil {
return nil, errors.Wrap(err, "error resolving follow request object")
}
image, _ := url.Parse(result.Image.String)
var disabledAt *time.Time
@@ -89,6 +111,7 @@ func GetFollower(iri string) (*apmodels.ActivityPubActor, error) {
Image: image,
FollowRequestIri: followIRI,
DisabledAt: disabledAt,
RequestObject: followRequestObject,
}
return &follower, nil

View File

@@ -10,7 +10,7 @@ import (
"github.com/go-fed/activity/streams/vocab"
"github.com/owncast/owncast/activitypub/apmodels"
"github.com/owncast/owncast/activitypub/crypto"
"github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/persistence/configrepository"
"github.com/pkg/errors"
log "github.com/sirupsen/logrus"
)
@@ -47,11 +47,12 @@ func Resolve(c context.Context, data []byte, callbacks ...interface{}) error {
// ResolveIRI will resolve an IRI ahd call the correct callback for the resolved type.
func ResolveIRI(c context.Context, iri string, callbacks ...interface{}) error {
configRepository := configrepository.Get()
log.Debugln("Resolving", iri)
req, _ := http.NewRequest(http.MethodGet, iri, nil)
actor := apmodels.MakeLocalIRIForAccount(data.GetDefaultFederationUsername())
actor := apmodels.MakeLocalIRIForAccount(configRepository.GetDefaultFederationUsername())
if err := crypto.SignRequest(req, nil, actor); err != nil {
return err
}

View File

@@ -2,14 +2,10 @@ package workerpool
import (
"net/http"
"runtime"
log "github.com/sirupsen/logrus"
)
// workerPoolSize defines the number of concurrent HTTP ActivityPub requests.
var workerPoolSize = runtime.GOMAXPROCS(0)
// Job struct bundling the ActivityPub and the payload in one struct.
type Job struct {
request *http.Request
@@ -18,8 +14,8 @@ type Job struct {
var queue chan Job
// InitOutboundWorkerPool starts n go routines that await ActivityPub jobs.
func InitOutboundWorkerPool() {
queue = make(chan Job)
func InitOutboundWorkerPool(workerPoolSize int) {
queue = make(chan Job, workerPoolSize)
// start workers
for i := 1; i <= workerPoolSize; i++ {
@@ -29,8 +25,13 @@ func InitOutboundWorkerPool() {
// AddToOutboundQueue will queue up an outbound http request.
func AddToOutboundQueue(req *http.Request) {
select {
case queue <- Job{req}:
default:
log.Debugln("Outbound ActivityPub job queue is full")
queue <- Job{req} // will block until received by a worker at this point
}
log.Tracef("Queued request for ActivityPub destination %s", req.RequestURI)
queue <- Job{req}
}
func worker(workerID int, queue <-chan Job) {

View File

@@ -11,7 +11,7 @@ import (
"sync"
"time"
"github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/persistence/configrepository"
"github.com/owncast/owncast/utils"
"github.com/pkg/errors"
log "github.com/sirupsen/logrus"
@@ -47,6 +47,8 @@ func setupExpiredRequestPruner() {
// StartAuthFlow will begin the IndieAuth flow by generating an auth request.
func StartAuthFlow(authHost, userID, accessToken, displayName string) (*url.URL, error) {
configRepository := configrepository.Get()
// Limit the number of pending requests
if len(pendingAuthRequests) >= maxPendingRequests {
return nil, errors.New("Please try again later. Too many pending requests.")
@@ -68,7 +70,7 @@ func StartAuthFlow(authHost, userID, accessToken, displayName string) (*url.URL,
return nil, errors.New("only servers secured with https are supported")
}
serverURL := data.GetServerURL()
serverURL := configRepository.GetServerURL()
if serverURL == "" {
return nil, errors.New("Owncast server URL must be set when using auth")
}

View File

@@ -4,7 +4,7 @@ import (
"fmt"
"time"
"github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/persistence/configrepository"
"github.com/pkg/errors"
"github.com/teris-io/shortid"
)
@@ -70,6 +70,8 @@ func StartServerAuth(clientID, redirectURI, codeChallenge, state, me string) (*S
// CompleteServerAuth will verify that the values provided in the final step
// of the IndieAuth flow are correct, and return some basic profile info.
func CompleteServerAuth(code, redirectURI, clientID string, codeVerifier string) (*ServerProfileResponse, error) {
configRepository := configrepository.Get()
request, pending := pendingServerAuthRequests[code]
if !pending {
return nil, errors.New("no pending authentication request")
@@ -89,11 +91,11 @@ func CompleteServerAuth(code, redirectURI, clientID string, codeVerifier string)
}
response := ServerProfileResponse{
Me: data.GetServerURL(),
Me: configRepository.GetServerURL(),
Profile: ServerProfile{
Name: data.GetServerName(),
URL: data.GetServerURL(),
Photo: fmt.Sprintf("%s/%s", data.GetServerURL(), data.GetLogoPath()),
Name: configRepository.GetServerName(),
URL: configRepository.GetServerURL(),
Photo: fmt.Sprintf("%s/%s", configRepository.GetServerURL(), configRepository.GetLogoPath()),
},
}

View File

@@ -4,7 +4,7 @@ import "path/filepath"
const (
// StaticVersionNumber is the version of Owncast that is used when it's not overwritten via build-time settings.
StaticVersionNumber = "0.2.0" // Shown when you build from develop
StaticVersionNumber = "0.2.2" // Shown when you build from develop
// FfmpegSuggestedVersion is the version of ffmpeg we suggest.
FfmpegSuggestedVersion = "v4.1.5" // Requires the v
// DataDirectory is the directory we save data to.

View File

@@ -4,6 +4,7 @@ import (
"time"
"github.com/owncast/owncast/models"
"github.com/owncast/owncast/webserver/handlers/generated"
)
// Defaults will hold default configuration values.
@@ -25,7 +26,7 @@ type Defaults struct {
WebServerIP string
Name string
AdminPassword string
StreamKeys []models.StreamKey
StreamKeys []generated.StreamKey
StreamVariants []models.StreamOutputVariant
@@ -43,14 +44,16 @@ type Defaults struct {
// GetDefaults will return default configuration values.
func GetDefaults() Defaults {
defaultStreamKey := "abc123"
defaultStreamKeyComment := "Default stream key"
return Defaults{
Name: "New Owncast Server",
Summary: "This is a new live video streaming server powered by Owncast.",
ServerWelcomeMessage: "",
Logo: "logo.svg",
AdminPassword: "abc123",
StreamKeys: []models.StreamKey{
{Key: "abc123", Comment: "Default stream key"},
StreamKeys: []generated.StreamKey{
{Key: &defaultStreamKey, Comment: &defaultStreamKeyComment},
},
Tags: []string{
"owncast",

View File

@@ -7,8 +7,9 @@ import (
"github.com/owncast/owncast/config"
"github.com/owncast/owncast/core/chat/events"
"github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/models"
"github.com/owncast/owncast/persistence/chatmessagerepository"
"github.com/owncast/owncast/persistence/configrepository"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
log "github.com/sirupsen/logrus"
@@ -23,6 +24,8 @@ var (
func Start(getStatusFunc func() models.Status) error {
setupPersistence()
configRepository := configrepository.Get()
getStatus = getStatusFunc
_server = NewChat()
@@ -35,7 +38,7 @@ func Start(getStatusFunc func() models.Status) error {
Help: "The number of chat messages incremented over time.",
ConstLabels: map[string]string{
"version": config.VersionNumber,
"host": data.GetServerURL(),
"host": configRepository.GetServerURL(),
},
})
@@ -101,7 +104,8 @@ func SendSystemMessage(text string, ephemeral bool) error {
}
if !ephemeral {
saveEvent(message.ID, nil, message.Body, message.GetMessageType(), nil, message.Timestamp, nil, nil, nil, nil)
chatMessageRepository := chatmessagerepository.Get()
chatMessageRepository.SaveEvent(message.ID, nil, message.Body, message.GetMessageType(), nil, message.Timestamp, nil, nil, nil, nil)
}
return nil
@@ -129,7 +133,8 @@ func SendFediverseAction(eventType string, userAccountName string, image *string
return err
}
saveFederatedAction(message)
chatMessageRepository := chatmessagerepository.Get()
chatMessageRepository.SaveFederatedAction(message)
return nil
}
@@ -150,7 +155,8 @@ func SendSystemAction(text string, ephemeral bool) error {
}
if !ephemeral {
saveEvent(message.ID, nil, message.Body, message.GetMessageType(), nil, message.Timestamp, nil, nil, nil, nil)
chatMessageRepository := chatmessagerepository.Get()
chatMessageRepository.SaveEvent(message.ID, nil, message.Body, message.GetMessageType(), nil, message.Timestamp, nil, nil, nil, nil)
}
return nil

View File

@@ -13,8 +13,8 @@ import (
"github.com/gorilla/websocket"
"github.com/owncast/owncast/config"
"github.com/owncast/owncast/core/chat/events"
"github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/models"
"github.com/owncast/owncast/persistence/configrepository"
"github.com/owncast/owncast/services/geoip"
)
@@ -133,7 +133,9 @@ func (c *Client) readPump() {
}
// Check if this message passes the optional language filter
if data.GetChatSlurFilterEnabled() && !c.messageFilter.Allow(string(message)) {
configRepository := configrepository.Get()
if configRepository.GetChatSlurFilterEnabled() && !c.messageFilter.Allow(string(message)) {
c.sendAction("Sorry, that message contained language that is not allowed in this chat.")
continue
}
@@ -209,9 +211,11 @@ func (c *Client) close() {
}
func (c *Client) passesRateLimit() bool {
configRepository := configrepository.Get()
// If spam rate limiting is disabled, or the user is a moderator, always
// allow the message.
if !data.GetChatSpamProtectionEnabled() || c.User.IsModerator() {
if !configRepository.GetChatSpamProtectionEnabled() || c.User.IsModerator() {
return true
}

View File

@@ -8,8 +8,9 @@ import (
"github.com/owncast/owncast/config"
"github.com/owncast/owncast/core/chat/events"
"github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/core/webhooks"
"github.com/owncast/owncast/persistence/chatmessagerepository"
"github.com/owncast/owncast/persistence/configrepository"
"github.com/owncast/owncast/persistence/userrepository"
"github.com/owncast/owncast/utils"
log "github.com/sirupsen/logrus"
@@ -22,10 +23,12 @@ func (s *Server) userNameChanged(eventData chatClientEvent) {
return
}
configRepository := configrepository.Get()
proposedUsername := receivedEvent.NewName
// Check if name is on the blocklist
blocklist := data.GetForbiddenUsernameList()
blocklist := configRepository.GetForbiddenUsernameList()
// Names have a max length
proposedUsername = utils.MakeSafeStringOfLength(proposedUsername, config.MaxChatDisplayNameLength)
@@ -170,8 +173,8 @@ func (s *Server) userMessageSent(eventData chatClientEvent) {
// Send chat message sent webhook
webhooks.SendChatEvent(&event)
chatMessagesSentCounter.Inc()
SaveUserMessage(event)
chatMessageRepository := chatmessagerepository.Get()
chatMessageRepository.SaveUserMessage(event)
eventData.client.MessageCount++
}

View File

@@ -162,9 +162,9 @@ func loadEmoji() {
if err != nil {
return
}
emojiHTML[strings.ToLower(emojiList[i].Name)] = buf.String()
emojiHTML[strings.ToLower(*emojiList[i].Name)] = buf.String()
emoji := emojiDef.NewEmoji(emojiList[i].Name, nil, strings.ToLower(emojiList[i].Name))
emoji := emojiDef.NewEmoji(*emojiList[i].Name, nil, strings.ToLower(*emojiList[i].Name))
emojiArr = append(emojiArr, emoji)
}

View File

@@ -1,6 +1,8 @@
package events
import "github.com/owncast/owncast/core/data"
import (
"github.com/owncast/owncast/persistence/configrepository"
)
// FediverseEngagementEvent is a message displayed in chat on representing an action on the Fediverse.
type FediverseEngagementEvent struct {
@@ -13,6 +15,8 @@ type FediverseEngagementEvent struct {
// GetBroadcastPayload will return the object to send to all chat users.
func (e *FediverseEngagementEvent) GetBroadcastPayload() EventPayload {
configRepository := configrepository.Get()
return EventPayload{
"id": e.ID,
"timestamp": e.Timestamp,
@@ -22,7 +26,7 @@ func (e *FediverseEngagementEvent) GetBroadcastPayload() EventPayload {
"title": e.UserAccountName,
"link": e.Link,
"user": EventPayload{
"displayName": data.GetServerName(),
"displayName": configRepository.GetServerName(),
},
}
}

View File

@@ -1,6 +1,8 @@
package events
import "github.com/owncast/owncast/core/data"
import (
"github.com/owncast/owncast/persistence/configrepository"
)
// SystemMessageEvent is a message displayed in chat on behalf of the server.
type SystemMessageEvent struct {
@@ -10,13 +12,15 @@ type SystemMessageEvent struct {
// GetBroadcastPayload will return the object to send to all chat users.
func (e *SystemMessageEvent) GetBroadcastPayload() EventPayload {
configRepository := configrepository.Get()
return EventPayload{
"id": e.ID,
"timestamp": e.Timestamp,
"body": e.Body,
"type": SystemMessageSent,
"user": EventPayload{
"displayName": data.GetServerName(),
"displayName": configRepository.GetServerName(),
},
}
}

View File

@@ -5,13 +5,15 @@ import (
"github.com/owncast/owncast/core/chat/events"
"github.com/owncast/owncast/core/webhooks"
"github.com/owncast/owncast/persistence/chatmessagerepository"
log "github.com/sirupsen/logrus"
)
// SetMessagesVisibility will set the visibility of multiple messages by ID.
func SetMessagesVisibility(messageIDs []string, visibility bool) error {
// Save new message visibility
if err := saveMessageVisibility(messageIDs, visibility); err != nil {
chatMessageRepository := chatmessagerepository.Get()
if err := chatMessageRepository.SetMessageVisibilityForMessageIDs(messageIDs, visibility); err != nil {
log.Errorln(err)
return err
}

View File

@@ -1,30 +1,25 @@
package chat
import (
"context"
"database/sql"
"strings"
"time"
"github.com/owncast/owncast/core/chat/events"
"github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/models"
"github.com/owncast/owncast/persistence/authrepository"
"github.com/owncast/owncast/persistence/tables"
log "github.com/sirupsen/logrus"
)
var _datastore *data.Datastore
const (
maxBacklogHours = 2 // Keep backlog max hours worth of messages
maxBacklogNumber = 50 // Return max number of messages in history request
maxBacklogHours = 2 // Keep backlog max hours worth of messages
)
func setupPersistence() {
_datastore = data.GetDatastore()
tables.CreateMessagesTable(_datastore.DB)
data.CreateBanIPTable(_datastore.DB)
authRepository := authrepository.Get()
authRepository.CreateBanIPTable(_datastore.DB)
chatDataPruner := time.NewTicker(5 * time.Minute)
go func() {
@@ -34,456 +29,3 @@ func setupPersistence() {
}
}()
}
// SaveUserMessage will save a single chat event to the messages database.
func SaveUserMessage(event events.UserMessageEvent) {
saveEvent(event.ID, &event.User.ID, event.Body, event.Type, event.HiddenAt, event.Timestamp, nil, nil, nil, nil)
}
func saveFederatedAction(event events.FediverseEngagementEvent) {
saveEvent(event.ID, nil, event.Body, event.Type, nil, event.Timestamp, event.Image, &event.Link, &event.UserAccountName, nil)
}
// nolint: unparam
func saveEvent(id string, userID *string, body string, eventType string, hidden *time.Time, timestamp time.Time, image *string, link *string, title *string, subtitle *string) {
defer func() {
_historyCache = nil
}()
tx, err := _datastore.DB.Begin()
if err != nil {
log.Errorln("error saving", eventType, err)
return
}
defer tx.Rollback() // nolint
stmt, err := tx.Prepare("INSERT INTO messages(id, user_id, body, eventType, hidden_at, timestamp, image, link, title, subtitle) values(?, ?, ?, ?, ?, ?, ?, ?, ?, ?)")
if err != nil {
log.Errorln("error saving", eventType, err)
return
}
defer stmt.Close()
if _, err = stmt.Exec(id, userID, body, eventType, hidden, timestamp, image, link, title, subtitle); err != nil {
log.Errorln("error saving", eventType, err)
return
}
if err = tx.Commit(); err != nil {
log.Errorln("error saving", eventType, err)
return
}
}
func makeUserMessageEventFromRowData(row rowData) events.UserMessageEvent {
scopes := ""
if row.userScopes != nil {
scopes = *row.userScopes
}
previousUsernames := []string{}
if row.previousUsernames != nil {
previousUsernames = strings.Split(*row.previousUsernames, ",")
}
displayName := ""
if row.userDisplayName != nil {
displayName = *row.userDisplayName
}
displayColor := 0
if row.userDisplayColor != nil {
displayColor = *row.userDisplayColor
}
createdAt := time.Time{}
if row.userCreatedAt != nil {
createdAt = *row.userCreatedAt
}
isBot := (row.userType != nil && *row.userType == "API")
scopeSlice := strings.Split(scopes, ",")
u := models.User{
ID: *row.userID,
DisplayName: displayName,
DisplayColor: displayColor,
CreatedAt: createdAt,
DisabledAt: row.userDisabledAt,
NameChangedAt: row.userNameChangedAt,
PreviousNames: previousUsernames,
AuthenticatedAt: row.userAuthenticatedAt,
Authenticated: row.userAuthenticatedAt != nil,
Scopes: scopeSlice,
IsBot: isBot,
}
message := events.UserMessageEvent{
Event: events.Event{
Type: row.eventType,
ID: row.id,
Timestamp: row.timestamp,
},
UserEvent: events.UserEvent{
User: &u,
HiddenAt: row.hiddenAt,
},
MessageEvent: events.MessageEvent{
Body: row.body,
RawBody: row.body,
},
}
return message
}
func makeSystemMessageChatEventFromRowData(row rowData) events.SystemMessageEvent {
message := events.SystemMessageEvent{
Event: events.Event{
Type: row.eventType,
ID: row.id,
Timestamp: row.timestamp,
},
MessageEvent: events.MessageEvent{
Body: row.body,
RawBody: row.body,
},
}
return message
}
func makeActionMessageChatEventFromRowData(row rowData) events.ActionEvent {
message := events.ActionEvent{
Event: events.Event{
Type: row.eventType,
ID: row.id,
Timestamp: row.timestamp,
},
MessageEvent: events.MessageEvent{
Body: row.body,
RawBody: row.body,
},
}
return message
}
func makeFederatedActionChatEventFromRowData(row rowData) events.FediverseEngagementEvent {
message := events.FediverseEngagementEvent{
Event: events.Event{
Type: row.eventType,
ID: row.id,
Timestamp: row.timestamp,
},
MessageEvent: events.MessageEvent{
Body: row.body,
RawBody: row.body,
},
Image: row.image,
Link: *row.link,
UserAccountName: *row.title,
}
return message
}
type rowData struct {
timestamp time.Time
image *string
previousUsernames *string
userDisplayName *string
userDisplayColor *int
userID *string
title *string
subtitle *string
link *string
userType *string
userScopes *string
hiddenAt *time.Time
userCreatedAt *time.Time
userDisabledAt *time.Time
userAuthenticatedAt *time.Time
userNameChangedAt *time.Time
body string
eventType models.EventType
id string
}
func getChat(rows *sql.Rows) ([]interface{}, error) {
history := make([]interface{}, 0)
for rows.Next() {
row := rowData{}
// Convert a database row into a chat event
if err := rows.Scan(
&row.id,
&row.userID,
&row.body,
&row.title,
&row.subtitle,
&row.image,
&row.link,
&row.eventType,
&row.hiddenAt,
&row.timestamp,
&row.userDisplayName,
&row.userDisplayColor,
&row.userCreatedAt,
&row.userDisabledAt,
&row.previousUsernames,
&row.userNameChangedAt,
&row.userAuthenticatedAt,
&row.userScopes,
&row.userType,
); err != nil {
return nil, err
}
var message interface{}
switch row.eventType {
case events.MessageSent:
message = makeUserMessageEventFromRowData(row)
case events.SystemMessageSent:
message = makeSystemMessageChatEventFromRowData(row)
case events.ChatActionSent:
message = makeActionMessageChatEventFromRowData(row)
case events.FediverseEngagementFollow:
message = makeFederatedActionChatEventFromRowData(row)
case events.FediverseEngagementLike:
message = makeFederatedActionChatEventFromRowData(row)
case events.FediverseEngagementRepost:
message = makeFederatedActionChatEventFromRowData(row)
}
history = append(history, message)
}
return history, nil
}
var _historyCache *[]interface{}
// GetChatModerationHistory will return all the chat messages suitable for moderation purposes.
func GetChatModerationHistory() []interface{} {
if _historyCache != nil {
return *_historyCache
}
tx, err := _datastore.DB.Begin()
if err != nil {
log.Errorln("error fetching chat moderation history", err)
return nil
}
defer tx.Rollback() // nolint
// Get all messages regardless of visibility
query := "SELECT messages.id, user_id, body, title, subtitle, image, link, eventType, hidden_at, timestamp, display_name, display_color, created_at, disabled_at, previous_names, namechanged_at, authenticated_at, scopes, type FROM messages INNER JOIN users ON messages.user_id = users.id ORDER BY timestamp DESC"
stmt, err := tx.Prepare(query)
if err != nil {
log.Errorln("error fetching chat moderation history", err)
return nil
}
rows, err := stmt.Query()
if err != nil {
log.Errorln("error fetching chat moderation history", err)
return nil
}
defer stmt.Close()
defer rows.Close()
result, err := getChat(rows)
if err != nil {
log.Errorln(err)
log.Errorln("There is a problem enumerating chat message rows. Please report this:", query)
return nil
}
_historyCache = &result
if err = tx.Commit(); err != nil {
log.Errorln("error fetching chat moderation history", err)
return nil
}
return result
}
// GetChatHistory will return all the chat messages suitable for returning as user-facing chat history.
func GetChatHistory() []interface{} {
tx, err := _datastore.DB.Begin()
if err != nil {
log.Errorln("error fetching chat history", err)
return nil
}
defer tx.Rollback() // nolint
// Get all visible messages
query := "SELECT messages.id, messages.user_id, messages.body, messages.title, messages.subtitle, messages.image, messages.link, messages.eventType, messages.hidden_at, messages.timestamp, users.display_name, users.display_color, users.created_at, users.disabled_at, users.previous_names, users.namechanged_at, users.authenticated_at, users.scopes, users.type FROM users JOIN messages ON users.id = messages.user_id WHERE hidden_at IS NULL AND disabled_at IS NULL ORDER BY timestamp DESC LIMIT ?"
stmt, err := tx.Prepare(query)
if err != nil {
log.Errorln("error fetching chat history", err)
return nil
}
rows, err := stmt.Query(maxBacklogNumber)
if err != nil {
log.Errorln("error fetching chat history", err)
return nil
}
defer stmt.Close()
defer rows.Close()
m, err := getChat(rows)
if err != nil {
log.Errorln(err)
log.Errorln("There is a problem enumerating chat message rows. Please report this:", query)
return nil
}
if err = tx.Commit(); err != nil {
log.Errorln("error fetching chat history", err)
return nil
}
// Invert order of messages
for i, j := 0, len(m)-1; i < j; i, j = i+1, j-1 {
m[i], m[j] = m[j], m[i]
}
return m
}
// GetMessagesFromUser returns chat messages that were sent by a specific user.
func GetMessagesFromUser(userID string) ([]events.UserMessageEvent, error) {
query, err := _datastore.GetQueries().GetMessagesFromUser(context.Background(), sql.NullString{String: userID, Valid: true})
if err != nil {
return nil, err
}
results := make([]events.UserMessageEvent, len(query))
for i, row := range query {
results[i] = events.UserMessageEvent{
Event: events.Event{
Timestamp: row.Timestamp.Time,
ID: row.ID,
},
MessageEvent: events.MessageEvent{
Body: row.Body.String,
},
}
}
return results, nil
}
// SetMessageVisibilityForUserID will bulk change the visibility of messages for a user
// and then send out visibility changed events to chat clients.
func SetMessageVisibilityForUserID(userID string, visible bool) error {
defer func() {
_historyCache = nil
}()
tx, err := _datastore.DB.Begin()
if err != nil {
log.Errorln("error while setting message visibility", err)
return nil
}
defer tx.Rollback() // nolint
query := "SELECT messages.id, user_id, body, title, subtitle, image, link, eventType, hidden_at, timestamp, display_name, display_color, created_at, disabled_at, previous_names, namechanged_at, authenticated_at, scopes, type FROM messages INNER JOIN users ON messages.user_id = users.id WHERE user_id IS ?"
stmt, err := tx.Prepare(query)
if err != nil {
log.Errorln("error while setting message visibility", err)
return nil
}
rows, err := stmt.Query(userID)
if err != nil {
log.Errorln("error while setting message visibility", err)
return nil
}
defer stmt.Close()
defer rows.Close()
// Get a list of IDs to send to the connected clients to hide
ids := make([]string, 0)
messages, err := getChat(rows)
if err != nil {
log.Errorln(err)
log.Errorln("There is a problem enumerating chat message rows. Please report this:", query)
return nil
}
if len(messages) == 0 {
return nil
}
for _, message := range messages {
ids = append(ids, message.(events.UserMessageEvent).ID)
}
if err = tx.Commit(); err != nil {
log.Errorln("error while setting message visibility ", err)
return nil
}
// Tell the clients to hide/show these messages.
return SetMessagesVisibility(ids, visible)
}
func saveMessageVisibility(messageIDs []string, visible bool) error {
defer func() {
_historyCache = nil
}()
_datastore.DbLock.Lock()
defer _datastore.DbLock.Unlock()
tx, err := _datastore.DB.Begin()
if err != nil {
return err
}
// nolint:gosec
stmt, err := tx.Prepare("UPDATE messages SET hidden_at=? WHERE id IN (?" + strings.Repeat(",?", len(messageIDs)-1) + ")")
if err != nil {
return err
}
defer stmt.Close()
var hiddenAt *time.Time
if !visible {
now := time.Now()
hiddenAt = &now
} else {
hiddenAt = nil
}
args := make([]interface{}, len(messageIDs)+1)
args[0] = hiddenAt
for i, id := range messageIDs {
args[i+1] = id
}
if _, err = stmt.Exec(args...); err != nil {
return err
}
if err = tx.Commit(); err != nil {
return err
}
return nil
}

View File

@@ -13,9 +13,10 @@ import (
"github.com/owncast/owncast/config"
"github.com/owncast/owncast/core/chat/events"
"github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/core/webhooks"
"github.com/owncast/owncast/models"
"github.com/owncast/owncast/persistence/authrepository"
"github.com/owncast/owncast/persistence/configrepository"
"github.com/owncast/owncast/persistence/userrepository"
"github.com/owncast/owncast/services/geoip"
"github.com/owncast/owncast/utils"
@@ -95,7 +96,9 @@ func (s *Server) Addclient(conn *websocket.Conn, user *models.User, accessToken
ConnectedAt: time.Now(),
}
shouldSendJoinedMessages := data.GetChatJoinPartMessagesEnabled()
configRepository := configrepository.Get()
shouldSendJoinedMessages := configRepository.GetChatJoinPartMessagesEnabled()
// If there are existing clients connected for this user do not send
// a user joined message. Do not put this under a mutex, as
@@ -186,8 +189,10 @@ func (s *Server) sendUserPartedMessage(c *Client) {
userPartEvent.User = c.User
userPartEvent.ClientID = c.Id
configRepository := configrepository.Get()
// If part messages are disabled.
if data.GetChatJoinPartMessagesEnabled() {
if configRepository.GetChatJoinPartMessagesEnabled() {
if err := s.Broadcast(userPartEvent.GetBroadcastPayload()); err != nil {
log.Errorln("error sending chat part message", err)
}
@@ -198,14 +203,17 @@ func (s *Server) sendUserPartedMessage(c *Client) {
// HandleClientConnection is fired when a single client connects to the websocket.
func (s *Server) HandleClientConnection(w http.ResponseWriter, r *http.Request) {
if data.GetChatDisabled() {
configRepository := configrepository.Get()
authRepository := authrepository.Get()
if configRepository.GetChatDisabled() {
_, _ = w.Write([]byte(events.ChatDisabled))
return
}
ipAddress := utils.GetIPAddressFromRequest(r)
// Check if this client's IP address is banned. If so send a rejection.
if blocked, err := data.IsIPAddressBanned(ipAddress); blocked {
if blocked, err := authRepository.IsIPAddressBanned(ipAddress); blocked {
log.Debugln("Client ip address has been blocked. Rejecting.")
w.WriteHeader(http.StatusForbidden)
@@ -377,12 +385,14 @@ func SendActionToUser(userID string, text string) error {
}
func (s *Server) eventReceived(event chatClientEvent) {
configRepository := configrepository.Get()
c := event.client
u := c.User
// If established chat user only mode is enabled and the user is not old
// enough then reject this event and send them an informative message.
if u != nil && data.GetChatEstbalishedUsersOnlyMode() && time.Since(event.client.User.CreatedAt) < config.GetDefaults().ChatEstablishedUserModeTimeDuration && !u.IsModerator() {
if u != nil && configRepository.GetChatEstbalishedUsersOnlyMode() && time.Since(event.client.User.CreatedAt) < config.GetDefaults().ChatEstablishedUserModeTimeDuration && !u.IsModerator() {
s.sendActionToClient(c, "You have not been an established chat participant long enough to take part in chat. Please enjoy the stream and try again later.")
return
}
@@ -409,10 +419,12 @@ func (s *Server) eventReceived(event chatClientEvent) {
}
func (s *Server) sendWelcomeMessageToClient(c *Client) {
configRepository := configrepository.Get()
// Add an artificial delay so people notice this message come in.
time.Sleep(7 * time.Second)
welcomeMessage := utils.RenderSimpleMarkdown(data.GetServerWelcomeMessage())
welcomeMessage := utils.RenderSimpleMarkdown(configRepository.GetServerWelcomeMessage())
if welcomeMessage != "" {
s.sendSystemMessageToClient(c, welcomeMessage)
@@ -420,7 +432,9 @@ func (s *Server) sendWelcomeMessageToClient(c *Client) {
}
func (s *Server) sendAllWelcomeMessage() {
welcomeMessage := utils.RenderSimpleMarkdown(data.GetServerWelcomeMessage())
configRepository := configrepository.Get()
welcomeMessage := utils.RenderSimpleMarkdown(configRepository.GetServerWelcomeMessage())
if welcomeMessage != "" {
clientMessage := events.SystemMessageEvent{

View File

@@ -16,6 +16,7 @@ import (
"github.com/owncast/owncast/core/webhooks"
"github.com/owncast/owncast/models"
"github.com/owncast/owncast/notifications"
"github.com/owncast/owncast/persistence/configrepository"
"github.com/owncast/owncast/persistence/tables"
"github.com/owncast/owncast/utils"
"github.com/owncast/owncast/yp"
@@ -34,10 +35,10 @@ var (
// Start starts up the core processing.
func Start() error {
resetDirectories()
configRepository := configrepository.Get()
// configRepository.PopulateDefaults()
data.PopulateDefaults()
if err := data.VerifySettings(); err != nil {
if err := configRepository.VerifySettings(); err != nil {
log.Error(err)
return err
}
@@ -75,7 +76,7 @@ func Start() error {
// start the rtmp server
go rtmp.Start(setStreamAsConnected, setBroadcaster)
rtmpPort := data.GetRTMPPortNumber()
rtmpPort := configRepository.GetRTMPPortNumber()
if rtmpPort != 1935 {
log.Infof("RTMP is accepting inbound streams on port %d.", rtmpPort)
}
@@ -113,7 +114,8 @@ func transitionToOfflineVideoStreamContent() {
go _transcoder.Start(false)
// Copy the logo to be the thumbnail
logo := data.GetLogoPath()
configRepository := configrepository.Get()
logo := configRepository.GetLogoPath()
dst := filepath.Join(config.TempDir, "thumbnail.jpg")
if err = utils.Copy(filepath.Join("data", logo), dst); err != nil {
log.Warnln(err)
@@ -130,7 +132,8 @@ func resetDirectories() {
utils.CleanupDirectory(config.HLSStoragePath)
// Remove the previous thumbnail
logo := data.GetLogoPath()
configRepository := configrepository.Get()
logo := configRepository.GetLogoPath()
if utils.DoesFileExists(logo) {
err := utils.Copy(path.Join("data", logo), filepath.Join(config.DataDirectory, "thumbnail.jpg"))
if err != nil {

View File

@@ -1,13 +0,0 @@
package data
// GetFederatedInboxMap is a mapping between account names and their outbox.
func GetFederatedInboxMap() map[string]string {
return map[string]string{
GetDefaultFederationUsername(): GetDefaultFederationUsername(),
}
}
// GetDefaultFederationUsername will return the username used for sending federation activities.
func GetDefaultFederationUsername() string {
return GetFederationUsername()
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,23 +0,0 @@
package data
// GetPublicKey will return the public key.
func GetPublicKey() string {
value, _ := _datastore.GetString(publicKeyKey)
return value
}
// SetPublicKey will save the public key.
func SetPublicKey(key string) error {
return _datastore.SetString(publicKeyKey, key)
}
// GetPrivateKey will return the private key.
func GetPrivateKey() string {
value, _ := _datastore.GetString(privateKeyKey)
return value
}
// SetPrivateKey will save the private key.
func SetPrivateKey(key string) error {
return _datastore.SetString(privateKeyKey, key)
}

View File

@@ -75,7 +75,8 @@ func SetupPersistence(file string) error {
_, _ = db.Exec("pragma temp_store = memory")
_, _ = db.Exec("pragma wal_checkpoint(full)")
createWebhooksTable()
tables.CreateConfigTable(db)
tables.CreateWebhooksTable(db)
tables.CreateUsersTable(db)
tables.CreateAccessTokenTable(db)

View File

@@ -4,6 +4,8 @@ import (
"fmt"
"os"
"testing"
"github.com/owncast/owncast/models"
)
func TestMain(m *testing.M) {
@@ -89,7 +91,7 @@ func TestCustomType(t *testing.T) {
}
// Save config entry to the database
if err := _datastore.Save(ConfigEntry{&testStruct, testKey}); err != nil {
if err := _datastore.Save(models.ConfigEntry{&testStruct, testKey}); err != nil {
t.Error(err)
}
@@ -101,7 +103,7 @@ func TestCustomType(t *testing.T) {
// Get a typed struct out of it
var testResult TestStruct
if err := entryResult.getObject(&testResult); err != nil {
if err := entryResult.GetObject(&testResult); err != nil {
t.Error(err)
}
@@ -121,7 +123,7 @@ func TestStringMap(t *testing.T) {
}
// Save config entry to the database
if err := _datastore.Save(ConfigEntry{&testMap, testKey}); err != nil {
if err := _datastore.Save(models.ConfigEntry{Value: &testMap, Key: testKey}); err != nil {
t.Error(err)
}
@@ -131,7 +133,7 @@ func TestStringMap(t *testing.T) {
t.Error(err)
}
testResult, err := entryResult.getStringMap()
testResult, err := entryResult.GetStringMap()
if err != nil {
t.Error(err)
}

View File

@@ -5,12 +5,11 @@ import (
"database/sql"
"encoding/gob"
"sync"
"time"
// sqlite requires a blank import.
_ "github.com/mattn/go-sqlite3"
"github.com/owncast/owncast/config"
"github.com/owncast/owncast/db"
"github.com/owncast/owncast/models"
log "github.com/sirupsen/logrus"
)
@@ -21,7 +20,8 @@ type Datastore struct {
DbLock *sync.Mutex
}
func (ds *Datastore) warmCache() {
// WarmCache pre-caches all configuration values in memory.
func (ds *Datastore) WarmCache() {
log.Traceln("Warming config value cache")
res, err := ds.DB.Query("SELECT key, value FROM datastore")
@@ -46,10 +46,10 @@ func (ds *Datastore) GetQueries() *db.Queries {
}
// Get will query the database for the key and return the entry.
func (ds *Datastore) Get(key string) (ConfigEntry, error) {
func (ds *Datastore) Get(key string) (models.ConfigEntry, error) {
cachedValue, err := ds.GetCachedValue(key)
if err == nil {
return ConfigEntry{
return models.ConfigEntry{
Key: key,
Value: cachedValue,
}, nil
@@ -60,10 +60,10 @@ func (ds *Datastore) Get(key string) (ConfigEntry, error) {
row := ds.DB.QueryRow("SELECT key, value FROM datastore WHERE key = ? LIMIT 1", key)
if err := row.Scan(&resultKey, &resultValue); err != nil {
return ConfigEntry{}, err
return models.ConfigEntry{}, err
}
result := ConfigEntry{
result := models.ConfigEntry{
Key: resultKey,
Value: resultValue,
}
@@ -73,7 +73,7 @@ func (ds *Datastore) Get(key string) (ConfigEntry, error) {
}
// Save will save the ConfigEntry to the database.
func (ds *Datastore) Save(e ConfigEntry) error {
func (ds *Datastore) Save(e models.ConfigEntry) error {
ds.DbLock.Lock()
defer ds.DbLock.Unlock()
@@ -93,7 +93,6 @@ func (ds *Datastore) Save(e ConfigEntry) error {
return err
}
_, err = stmt.Exec(e.Key, dataGob.Bytes())
if err != nil {
return err
}
@@ -108,39 +107,11 @@ func (ds *Datastore) Save(e ConfigEntry) error {
return nil
}
// Setup will create the datastore table and perform initial initialization.
// Setup will perform initial initialization.
func (ds *Datastore) Setup() {
ds.cache = make(map[string][]byte)
ds.DB = GetDatabase()
ds.DbLock = &sync.Mutex{}
createTableSQL := `CREATE TABLE IF NOT EXISTS datastore (
"key" string NOT NULL PRIMARY KEY,
"value" BLOB,
"timestamp" DATE DEFAULT CURRENT_TIMESTAMP NOT NULL
);`
ds.MustExec(createTableSQL)
if !HasPopulatedDefaults() {
PopulateDefaults()
}
if !hasPopulatedFederationDefaults() {
if err := SetFederationGoLiveMessage(config.GetDefaults().FederationGoLiveMessage); err != nil {
log.Errorln(err)
}
if err := _datastore.SetBool("HAS_POPULATED_FEDERATION_DEFAULTS", true); err != nil {
log.Errorln(err)
}
}
// Set the server initialization date if needed.
if hasSetInitDate, _ := GetServerInitTime(); hasSetInitDate == nil || !hasSetInitDate.Valid {
_ = SetServerInitTime(time.Now())
}
migrateDatastoreValues(_datastore)
}
// Reset will delete all config entries in the datastore and start over.
@@ -156,8 +127,6 @@ func (ds *Datastore) Reset() {
if _, err = stmt.Exec(); err != nil {
log.Fatalln(err)
}
PopulateDefaults()
}
// GetDatastore returns the shared instance of the owncast datastore.

View File

@@ -1,54 +0,0 @@
package data
import (
"github.com/owncast/owncast/config"
"github.com/owncast/owncast/models"
)
// HasPopulatedDefaults will determine if the defaults have been inserted into the database.
func HasPopulatedDefaults() bool {
hasPopulated, err := _datastore.GetBool("HAS_POPULATED_DEFAULTS")
if err != nil {
return false
}
return hasPopulated
}
func hasPopulatedFederationDefaults() bool {
hasPopulated, err := _datastore.GetBool("HAS_POPULATED_FEDERATION_DEFAULTS")
if err != nil {
return false
}
return hasPopulated
}
// PopulateDefaults will set default values in the database.
func PopulateDefaults() {
_datastore.warmCache()
defaults := config.GetDefaults()
if HasPopulatedDefaults() {
return
}
_ = SetAdminPassword(defaults.AdminPassword)
_ = SetStreamKeys(defaults.StreamKeys)
_ = SetHTTPPortNumber(float64(defaults.WebServerPort))
_ = SetRTMPPortNumber(float64(defaults.RTMPServerPort))
_ = SetLogoPath(defaults.Logo)
_ = SetServerMetadataTags([]string{"owncast", "streaming"})
_ = SetServerSummary(defaults.Summary)
_ = SetServerWelcomeMessage("")
_ = SetServerName(defaults.Name)
_ = SetExtraPageBodyContent(defaults.PageBodyContent)
_ = SetFederationGoLiveMessage(defaults.FederationGoLiveMessage)
_ = SetSocialHandles([]models.SocialHandle{
{
Platform: "github",
URL: "https://github.com/owncast/owncast",
},
})
_ = _datastore.SetBool("HAS_POPULATED_DEFAULTS", true)
}

View File

@@ -10,16 +10,16 @@ import (
"time"
"github.com/owncast/owncast/config"
"github.com/owncast/owncast/models"
"github.com/owncast/owncast/static"
"github.com/owncast/owncast/utils"
"github.com/owncast/owncast/webserver/handlers/generated"
"github.com/pkg/errors"
log "github.com/sirupsen/logrus"
)
var (
emojiCacheMu sync.Mutex
emojiCacheData = make([]models.CustomEmoji, 0)
emojiCacheData = make([]generated.Emoji, 0)
emojiCacheModTime time.Time
)
@@ -51,7 +51,7 @@ func UpdateEmojiList(force bool) (time.Time, error) {
return modTime, fmt.Errorf("unable to open custom emoji directory")
}
emojiCacheData = make([]models.CustomEmoji, 0)
emojiCacheData = make([]generated.Emoji, 0)
walkFunction := func(path string, d os.DirEntry, err error) error {
if d == nil || d.IsDir() {
@@ -61,7 +61,7 @@ func UpdateEmojiList(force bool) (time.Time, error) {
emojiPath := filepath.Join(config.EmojiDir, path)
fileName := d.Name()
fileBase := fileName[:len(fileName)-len(filepath.Ext(fileName))]
singleEmoji := models.CustomEmoji{Name: fileBase, URL: emojiPath}
singleEmoji := generated.Emoji{Name: &fileBase, Url: &emojiPath}
emojiCacheData = append(emojiCacheData, singleEmoji)
return nil
}
@@ -76,7 +76,7 @@ func UpdateEmojiList(force bool) (time.Time, error) {
}
// GetEmojiList returns a list of custom emoji from the emoji directory.
func GetEmojiList() []models.CustomEmoji {
func GetEmojiList() []generated.Emoji {
_, err := UpdateEmojiList(false)
if err != nil {
return nil
@@ -88,7 +88,7 @@ func GetEmojiList() []models.CustomEmoji {
// return a copy of cache data, ensures underlying slice isn't affected
// by future update
emojiData := make([]models.CustomEmoji, len(emojiCacheData))
emojiData := make([]generated.Emoji, len(emojiCacheData))
copy(emojiData, emojiCacheData)
return emojiData

View File

@@ -1,82 +0,0 @@
package data
import (
"context"
"database/sql"
"github.com/owncast/owncast/db"
"github.com/owncast/owncast/models"
log "github.com/sirupsen/logrus"
)
// GetMessagesCount will return the number of messages in the database.
func GetMessagesCount() int64 {
query := `SELECT COUNT(*) FROM messages`
rows, err := _db.Query(query)
if err != nil || rows.Err() != nil {
return 0
}
defer rows.Close()
var count int64
for rows.Next() {
if err := rows.Scan(&count); err != nil {
return 0
}
}
return count
}
// CreateBanIPTable will create the IP ban table if needed.
func CreateBanIPTable(db *sql.DB) {
createTableSQL := ` CREATE TABLE IF NOT EXISTS ip_bans (
"ip_address" TEXT NOT NULL PRIMARY KEY,
"notes" TEXT,
"created_at" TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);`
stmt, err := db.Prepare(createTableSQL)
if err != nil {
log.Fatal("error creating ip ban table", err)
}
defer stmt.Close()
if _, err := stmt.Exec(); err != nil {
log.Fatal("error creating ip ban table", err)
}
}
// BanIPAddress will persist a new IP address ban to the datastore.
func BanIPAddress(address, note string) error {
return _datastore.GetQueries().BanIPAddress(context.Background(), db.BanIPAddressParams{
IpAddress: address,
Notes: sql.NullString{String: note, Valid: true},
})
}
// IsIPAddressBanned will return if an IP address has been previously blocked.
func IsIPAddressBanned(address string) (bool, error) {
blocked, error := _datastore.GetQueries().IsIPAddressBlocked(context.Background(), address)
return blocked > 0, error
}
// GetIPAddressBans will return all the banned IP addresses.
func GetIPAddressBans() ([]models.IPAddress, error) {
result, err := _datastore.GetQueries().GetIPAddressBans(context.Background())
if err != nil {
return nil, err
}
response := []models.IPAddress{}
for _, ip := range result {
response = append(response, models.IPAddress{
IPAddress: ip.IpAddress,
Notes: ip.Notes.String,
CreatedAt: ip.CreatedAt.Time,
})
}
return response, err
}
// RemoveIPAddressBan will remove a previously banned IP address.
func RemoveIPAddressBan(address string) error {
return _datastore.GetQueries().RemoveIPAddressBan(context.Background(), address)
}

View File

@@ -1,17 +1,19 @@
package data
import "github.com/owncast/owncast/models"
// GetStringSlice will return the string slice value for a key.
func (ds *Datastore) GetStringSlice(key string) ([]string, error) {
configEntry, err := ds.Get(key)
if err != nil {
return []string{}, err
}
return configEntry.getStringSlice()
return configEntry.GetStringSlice()
}
// SetStringSlice will set the string slice value for a key.
func (ds *Datastore) SetStringSlice(key string, value []string) error {
configEntry := ConfigEntry{value, key}
configEntry := models.ConfigEntry{Value: value, Key: key}
return ds.Save(configEntry)
}
@@ -21,12 +23,12 @@ func (ds *Datastore) GetString(key string) (string, error) {
if err != nil {
return "", err
}
return configEntry.getString()
return configEntry.GetString()
}
// SetString will set the string value for a key.
func (ds *Datastore) SetString(key string, value string) error {
configEntry := ConfigEntry{value, key}
configEntry := models.ConfigEntry{Value: value, Key: key}
return ds.Save(configEntry)
}
@@ -36,12 +38,12 @@ func (ds *Datastore) GetNumber(key string) (float64, error) {
if err != nil {
return 0, err
}
return configEntry.getNumber()
return configEntry.GetNumber()
}
// SetNumber will set the numeric value for a key.
func (ds *Datastore) SetNumber(key string, value float64) error {
configEntry := ConfigEntry{value, key}
configEntry := models.ConfigEntry{Value: value, Key: key}
return ds.Save(configEntry)
}
@@ -51,12 +53,12 @@ func (ds *Datastore) GetBool(key string) (bool, error) {
if err != nil {
return false, err
}
return configEntry.getBool()
return configEntry.GetBool()
}
// SetBool will set the boolean value for a key.
func (ds *Datastore) SetBool(key string, value bool) error {
configEntry := ConfigEntry{value, key}
configEntry := models.ConfigEntry{Value: value, Key: key}
return ds.Save(configEntry)
}
@@ -66,11 +68,11 @@ func (ds *Datastore) GetStringMap(key string) (map[string]string, error) {
if err != nil {
return map[string]string{}, err
}
return configEntry.getStringMap()
return configEntry.GetStringMap()
}
// SetStringMap will set the string map value for a key.
func (ds *Datastore) SetStringMap(key string, value map[string]string) error {
configEntry := ConfigEntry{value, key}
configEntry := models.ConfigEntry{Value: value, Key: key}
return ds.Save(configEntry)
}

View File

@@ -12,8 +12,9 @@ import (
"github.com/nareix/joy5/format/rtmp"
"github.com/owncast/owncast/config"
"github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/models"
"github.com/owncast/owncast/persistence/configrepository"
"github.com/owncast/owncast/webserver/handlers/generated"
)
var _hasInboundRTMPConnection = false
@@ -33,7 +34,9 @@ func Start(setStreamAsConnected func(*io.PipeReader), setBroadcaster func(models
_setStreamAsConnected = setStreamAsConnected
_setBroadcaster = setBroadcaster
port := data.GetRTMPPortNumber()
configRepository := configrepository.Get()
port := configRepository.GetRTMPPortNumber()
s := rtmp.NewServer()
var lis net.Listener
var err error
@@ -78,16 +81,18 @@ func HandleConn(c *rtmp.Conn, nc net.Conn) {
return
}
configRepository := configrepository.Get()
accessGranted := false
validStreamingKeys := data.GetStreamKeys()
validStreamingKeys := configRepository.GetStreamKeys()
// If a stream key override was specified then use that instead.
if config.TemporaryStreamKey != "" {
validStreamingKeys = []models.StreamKey{{Key: config.TemporaryStreamKey}}
validStreamingKeys = []generated.StreamKey{{Key: &config.TemporaryStreamKey}}
}
for _, key := range validStreamingKeys {
if secretMatch(key.Key, c.URL.Path) {
if key.Key != nil && secretMatch(*key.Key, c.URL.Path) {
accessGranted = true
break
}

View File

@@ -7,8 +7,8 @@ import (
log "github.com/sirupsen/logrus"
"github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/models"
"github.com/owncast/owncast/persistence/configrepository"
"github.com/owncast/owncast/services/geoip"
)
@@ -48,7 +48,8 @@ func IsStreamConnected() bool {
// Kind of a hack. It takes a handful of seconds between a RTMP connection and when HLS data is available.
// So account for that with an artificial buffer of four segments.
timeSinceLastConnected := time.Since(_stats.LastConnectTime.Time).Seconds()
waitTime := math.Max(float64(data.GetStreamLatencyLevel().SecondsPerSegment)*3.0, 7)
configRepository := configrepository.Get()
waitTime := math.Max(float64(configRepository.GetStreamLatencyLevel().SecondsPerSegment)*3.0, 7)
if timeSinceLastConnected < waitTime {
return false
}
@@ -75,7 +76,7 @@ func SetViewerActive(viewer *models.Viewer) {
l.Lock()
defer l.Unlock()
// Asynchronously, optionally, fetch GeoIP data.
// Asynchronously, optionally, fetch GeoIP configRepository.
go func(viewer *models.Viewer) {
viewer.Geo = _geoIPClient.GetGeoFromIP(viewer.IPAddress)
}(viewer)
@@ -111,27 +112,29 @@ func pruneViewerCount() {
}
func saveStats() {
if err := data.SetPeakOverallViewerCount(_stats.OverallMaxViewerCount); err != nil {
configRepository := configrepository.Get()
if err := configRepository.SetPeakOverallViewerCount(_stats.OverallMaxViewerCount); err != nil {
log.Errorln("error saving viewer count", err)
}
if err := data.SetPeakSessionViewerCount(_stats.SessionMaxViewerCount); err != nil {
if err := configRepository.SetPeakSessionViewerCount(_stats.SessionMaxViewerCount); err != nil {
log.Errorln("error saving viewer count", err)
}
if _stats.LastDisconnectTime != nil && _stats.LastDisconnectTime.Valid {
if err := data.SetLastDisconnectTime(_stats.LastDisconnectTime.Time); err != nil {
if err := configRepository.SetLastDisconnectTime(_stats.LastDisconnectTime.Time); err != nil {
log.Errorln("error saving disconnect time", err)
}
}
}
func getSavedStats() models.Stats {
savedLastDisconnectTime, _ := data.GetLastDisconnectTime()
configRepository := configrepository.Get()
savedLastDisconnectTime, _ := configRepository.GetLastDisconnectTime()
result := models.Stats{
ChatClients: make(map[string]models.Client),
Viewers: make(map[string]*models.Viewer),
SessionMaxViewerCount: data.GetPeakSessionViewerCount(),
OverallMaxViewerCount: data.GetPeakOverallViewerCount(),
SessionMaxViewerCount: configRepository.GetPeakSessionViewerCount(),
OverallMaxViewerCount: configRepository.GetPeakOverallViewerCount(),
LastDisconnectTime: savedLastDisconnectTime,
}

View File

@@ -2,8 +2,8 @@ package core
import (
"github.com/owncast/owncast/config"
"github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/models"
"github.com/owncast/owncast/persistence/configrepository"
)
// GetStatus gets the status of the system.
@@ -17,6 +17,7 @@ func GetStatus() models.Status {
viewerCount = len(_stats.Viewers)
}
configRepository := configrepository.Get()
return models.Status{
Online: IsStreamConnected(),
ViewerCount: viewerCount,
@@ -25,7 +26,7 @@ func GetStatus() models.Status {
LastDisconnectTime: _stats.LastDisconnectTime,
LastConnectTime: _stats.LastConnectTime,
VersionNumber: config.VersionNumber,
StreamTitle: data.GetStreamTitle(),
StreamTitle: configRepository.GetStreamTitle(),
}
}

View File

@@ -1,12 +1,13 @@
package core
import (
"github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/core/storageproviders"
"github.com/owncast/owncast/persistence/configrepository"
)
func setupStorage() error {
s3Config := data.GetS3Config()
configRepository := configrepository.Get()
s3Config := configRepository.GetS3Config()
if s3Config.Enabled {
_storage = storageproviders.NewS3Storage()

View File

@@ -5,9 +5,8 @@ import (
"path/filepath"
"sort"
"github.com/owncast/owncast/persistence/configrepository"
log "github.com/sirupsen/logrus"
"github.com/owncast/owncast/core/data"
)
// LocalStorage represents an instance of the local storage provider for HLS video.
@@ -22,7 +21,8 @@ func NewLocalStorage() *LocalStorage {
// Setup configures this storage provider.
func (s *LocalStorage) Setup() error {
s.host = data.GetVideoServingEndpoint()
configRepository := configrepository.Get()
s.host = configRepository.GetVideoServingEndpoint()
return nil
}
@@ -63,7 +63,8 @@ func (s *LocalStorage) Save(filePath string, retryCount int) (string, error) {
// Cleanup will remove old files from the storage provider.
func (s *LocalStorage) Cleanup() error {
// Determine how many files we should keep on disk
maxNumber := data.GetStreamLatencyLevel().SegmentCount
configRepository := configrepository.Get()
maxNumber := configRepository.GetStreamLatencyLevel().SegmentCount
buffer := 10
return localCleanup(maxNumber + buffer)
}

View File

@@ -11,7 +11,7 @@ import (
"sync"
"time"
"github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/persistence/configrepository"
"github.com/owncast/owncast/utils"
"github.com/pkg/errors"
log "github.com/sirupsen/logrus"
@@ -64,9 +64,9 @@ func NewS3Storage() *S3Storage {
// Setup sets up the s3 storage for saving the video to s3.
func (s *S3Storage) Setup() error {
log.Trace("Setting up S3 for external storage of video...")
s3Config := data.GetS3Config()
customVideoServingEndpoint := data.GetVideoServingEndpoint()
configRepository := configrepository.Get()
s3Config := configRepository.GetS3Config()
customVideoServingEndpoint := configRepository.GetVideoServingEndpoint()
if customVideoServingEndpoint != "" {
s.host = customVideoServingEndpoint
@@ -106,8 +106,9 @@ func (s *S3Storage) SegmentWritten(localFilePath string) {
averagePerformance := utils.GetAveragePerformance(performanceMonitorKey)
// Warn the user about long-running save operations
configRepository := configrepository.Get()
if averagePerformance != 0 {
if averagePerformance > float64(data.GetStreamLatencyLevel().SecondsPerSegment)*0.9 {
if averagePerformance > float64(configRepository.GetStreamLatencyLevel().SecondsPerSegment)*0.9 {
log.Warnln("Possible slow uploads: average upload S3 save duration", averagePerformance, "s. troubleshoot this issue by visiting https://owncast.online/docs/troubleshooting/")
}
}
@@ -220,7 +221,8 @@ func (s *S3Storage) Cleanup() error {
// RemoteCleanup will remove old files from the remote storage provider.
func (s *S3Storage) RemoteCleanup() error {
// Determine how many files we should keep on S3 storage
maxNumber := data.GetStreamLatencyLevel().SegmentCount
configRepository := configrepository.Get()
maxNumber := configRepository.GetStreamLatencyLevel().SegmentCount
buffer := 20
keys, err := s.getDeletableVideoSegmentsWithOffset(maxNumber + buffer)

View File

@@ -16,6 +16,7 @@ import (
"github.com/owncast/owncast/core/webhooks"
"github.com/owncast/owncast/models"
"github.com/owncast/owncast/notifications"
"github.com/owncast/owncast/persistence/configrepository"
"github.com/owncast/owncast/utils"
)
@@ -39,9 +40,11 @@ func setStreamAsConnected(rtmpOut *io.PipeReader) {
_stats.LastConnectTime = &now
_stats.SessionMaxViewerCount = 0
configRepository := configrepository.Get()
_currentBroadcast = &models.CurrentBroadcast{
LatencyLevel: data.GetStreamLatencyLevel(),
OutputSettings: data.GetStreamOutputVariants(),
LatencyLevel: configRepository.GetStreamLatencyLevel(),
OutputSettings: configRepository.GetStreamOutputVariants(),
}
StopOfflineCleanupTimer()
@@ -69,7 +72,7 @@ func setStreamAsConnected(rtmpOut *io.PipeReader) {
}()
go webhooks.SendStreamStatusEvent(models.StreamStarted)
selectedThumbnailVideoQualityIndex, isVideoPassthrough := data.FindHighestVideoQualityIndex(_currentBroadcast.OutputSettings)
selectedThumbnailVideoQualityIndex, isVideoPassthrough := configRepository.FindHighestVideoQualityIndex(_currentBroadcast.OutputSettings)
transcoder.StartThumbnailGenerator(segmentPath, selectedThumbnailVideoQualityIndex, isVideoPassthrough)
_ = chat.SendSystemAction("Stay tuned, the stream is **starting**!", true)
@@ -176,8 +179,9 @@ func startLiveStreamNotificationsTimer() context.CancelFunc {
return
}
configRepository := configrepository.Get()
// Send Fediverse message.
if data.GetFederationEnabled() {
if configRepository.GetFederationEnabled() {
log.Traceln("Sending Federated Go Live message.")
if err := activitypub.SendLive(); err != nil {
log.Errorln(err)

View File

@@ -27,6 +27,7 @@ var supportedCodecs = map[string]string{
(&Libx264Codec{}).Name(): "libx264",
(&OmxCodec{}).Name(): "omx",
(&VaapiCodec{}).Name(): "vaapi",
(&QuicksyncCodec{}).Name(): "qsv",
(&NvencCodec{}).Name(): "NVIDIA nvenc",
(&VideoToolboxCodec{}).Name(): "videotoolbox",
}
@@ -191,7 +192,7 @@ func (c *VaapiCodec) GlobalFlags() string {
// PixelFormat is the pixel format required for this codec.
func (c *VaapiCodec) PixelFormat() string {
return "vaapi_vld"
return "vaapi"
}
// Scaler is the scaler used for resizing the video in the transcoder.
@@ -201,7 +202,7 @@ func (c *VaapiCodec) Scaler() string {
// ExtraFilters are the extra filters required for this codec in the transcoder.
func (c *VaapiCodec) ExtraFilters() string {
return ""
return "hwupload=extra_hw_frames=64,format=vaapi"
}
// ExtraArguments are the extra arguments used with this codec in the transcoder.
@@ -317,17 +318,22 @@ func (c *QuicksyncCodec) DisplayName() string {
// GlobalFlags are the global flags used with this codec in the transcoder.
func (c *QuicksyncCodec) GlobalFlags() string {
return ""
flags := []string{
"-init_hw_device", "qsv=hw",
"-filter_hw_device", "hw",
}
return strings.Join(flags, " ")
}
// PixelFormat is the pixel format required for this codec.
func (c *QuicksyncCodec) PixelFormat() string {
return "nv12"
return "qsv"
}
// Scaler is the scaler used for resizing the video in the transcoder.
func (c *QuicksyncCodec) Scaler() string {
return ""
return "scale_qsv"
}
// ExtraArguments are the extra arguments used with this codec in the transcoder.
@@ -337,7 +343,7 @@ func (c *QuicksyncCodec) ExtraArguments() string {
// ExtraFilters are the extra filters required for this codec in the transcoder.
func (c *QuicksyncCodec) ExtraFilters() string {
return ""
return "hwupload=extra_hw_frames=64,format=qsv"
}
// VariantFlags returns a string representing a single variant processed by this codec.
@@ -348,16 +354,16 @@ func (c *QuicksyncCodec) VariantFlags(v *HLSVariant) string {
// GetPresetForLevel returns the string preset for this codec given an integer level.
func (c *QuicksyncCodec) GetPresetForLevel(l int) string {
presetMapping := map[int]string{
0: "ultrafast",
1: "superfast",
2: "veryfast",
3: "faster",
4: "fast",
0: "veryfast",
1: "fast",
2: "medium",
3: "slow",
4: "veryslow",
}
preset, ok := presetMapping[l]
if !ok {
defaultPreset := presetMapping[1]
defaultPreset := presetMapping[2]
log.Errorf("Invalid level for quicksync preset %d, defaulting to %s", l, defaultPreset)
return defaultPreset
}

View File

@@ -11,7 +11,7 @@ import (
log "github.com/sirupsen/logrus"
"github.com/owncast/owncast/config"
"github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/persistence/configrepository"
"github.com/owncast/owncast/utils"
)
@@ -88,9 +88,9 @@ func fireThumbnailGenerator(segmentPath string, variantIndex int) error {
if len(names) == 0 {
return nil
}
configRepository := configrepository.Get()
mostRecentFile := path.Join(framePath, names[0])
ffmpegPath := utils.ValidatedFfmpegPath(data.GetFfMpegPath())
ffmpegPath := utils.ValidatedFfmpegPath(configRepository.GetFfMpegPath())
outputFileTemp := path.Join(config.TempDir, "tempthumbnail.jpg")
thumbnailCmdFlags := []string{
@@ -120,7 +120,8 @@ func fireThumbnailGenerator(segmentPath string, variantIndex int) error {
}
func makeAnimatedGifPreview(sourceFile string, outputFile string) {
ffmpegPath := utils.ValidatedFfmpegPath(data.GetFfMpegPath())
configRepository := configrepository.Get()
ffmpegPath := utils.ValidatedFfmpegPath(configRepository.GetFfMpegPath())
outputFileTemp := path.Join(config.TempDir, "temppreview.gif")
// Filter is pulled from https://engineering.giphy.com/how-to-make-gifs-with-ffmpeg/

View File

@@ -12,9 +12,9 @@ import (
"github.com/teris-io/shortid"
"github.com/owncast/owncast/config"
"github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/logging"
"github.com/owncast/owncast/models"
"github.com/owncast/owncast/persistence/configrepository"
"github.com/owncast/owncast/utils"
)
@@ -229,8 +229,7 @@ func (t *Transcoder) getString() string {
"-hls_segment_filename", localListenerAddress + "/%v/stream-" + t.segmentIdentifier + "-%d.ts", // Send HLS segments back to us over HTTP
"-max_muxing_queue_size", "400", // Workaround for Too many packets error: https://trac.ffmpeg.org/ticket/6375?cversion=0
"-method PUT", // HLS results sent back to us will be over PUTs
"-http_persistent", "1", // Ensures persistent HTTP connections
"-method PUT", // HLS results sent back to us will be over PUTs
localListenerAddress + "/%v/stream.m3u8", // Send HLS playlists back to us over HTTP
}
@@ -275,15 +274,16 @@ func getVariantFromConfigQuality(quality models.StreamOutputVariant, index int)
// NewTranscoder will return a new Transcoder, populated by the config.
func NewTranscoder() *Transcoder {
ffmpegPath := utils.ValidatedFfmpegPath(data.GetFfMpegPath())
configRepository := configrepository.Get()
ffmpegPath := utils.ValidatedFfmpegPath(configRepository.GetFfMpegPath())
transcoder := new(Transcoder)
transcoder.ffmpegPath = ffmpegPath
transcoder.internalListenerPort = config.InternalHLSListenerPort
transcoder.currentStreamOutputSettings = data.GetStreamOutputVariants()
transcoder.currentLatencyLevel = data.GetStreamLatencyLevel()
transcoder.codec = getCodec(data.GetVideoCodec())
transcoder.currentStreamOutputSettings = configRepository.GetStreamOutputVariants()
transcoder.currentLatencyLevel = configRepository.GetStreamLatencyLevel()
transcoder.codec = getCodec(configRepository.GetVideoCodec())
transcoder.segmentOutputPath = config.HLSStoragePath
transcoder.playlistOutputPath = config.HLSStoragePath

View File

@@ -42,7 +42,7 @@ func TestFFmpegNvencCommand(t *testing.T) {
cmd := transcoder.getString()
expectedLogPath := filepath.Join("data", "logs", "transcoder.log")
expected := `FFREPORT=file="` + expectedLogPath + `":level=32 ` + transcoder.ffmpegPath + ` -hide_banner -loglevel warning -hwaccel cuda -fflags +genpts -flags +cgop -i fakecontent.flv -map v:0 -c:v:0 h264_nvenc -b:v:0 1008k -maxrate:v:0 1088k -g:v:0 90 -keyint_min:v:0 90 -r:v:0 30 -tune:v:0 ll -map a:0? -c:a:0 copy -preset p3 -map v:0 -c:v:1 h264_nvenc -b:v:1 3308k -maxrate:v:1 3572k -g:v:1 72 -keyint_min:v:1 72 -r:v:1 24 -tune:v:1 ll -map a:0? -c:a:1 copy -preset p5 -map v:0 -c:v:2 copy -map a:0? -c:a:2 copy -preset p1 -var_stream_map "v:0,a:0 v:1,a:1 v:2,a:2 " -f hls -hls_time 3 -hls_list_size 10 -hls_flags program_date_time+independent_segments+omit_endlist -segment_format_options mpegts_flags=mpegts_copyts=1 -pix_fmt yuv420p -sc_threshold 0 -master_pl_name stream.m3u8 -hls_segment_filename http://127.0.0.1:8123/%v/stream-jdoieGg-%d.ts -max_muxing_queue_size 400 -method PUT -http_persistent 1 http://127.0.0.1:8123/%v/stream.m3u8`
expected := `FFREPORT=file="` + expectedLogPath + `":level=32 ` + transcoder.ffmpegPath + ` -hide_banner -loglevel warning -hwaccel cuda -fflags +genpts -flags +cgop -i fakecontent.flv -map v:0 -c:v:0 h264_nvenc -b:v:0 1008k -maxrate:v:0 1088k -g:v:0 90 -keyint_min:v:0 90 -r:v:0 30 -tune:v:0 ll -map a:0? -c:a:0 copy -preset p3 -map v:0 -c:v:1 h264_nvenc -b:v:1 3308k -maxrate:v:1 3572k -g:v:1 72 -keyint_min:v:1 72 -r:v:1 24 -tune:v:1 ll -map a:0? -c:a:1 copy -preset p5 -map v:0 -c:v:2 copy -map a:0? -c:a:2 copy -preset p1 -var_stream_map "v:0,a:0 v:1,a:1 v:2,a:2 " -f hls -hls_time 3 -hls_list_size 10 -hls_flags program_date_time+independent_segments+omit_endlist -segment_format_options mpegts_flags=mpegts_copyts=1 -pix_fmt yuv420p -sc_threshold 0 -master_pl_name stream.m3u8 -hls_segment_filename http://127.0.0.1:8123/%v/stream-jdoieGg-%d.ts -max_muxing_queue_size 400 -method PUT http://127.0.0.1:8123/%v/stream.m3u8`
if cmd != expected {
t.Errorf("ffmpeg command does not match expected.\nGot %s\n, want: %s", cmd, expected)

View File

@@ -42,7 +42,7 @@ func TestFFmpegOmxCommand(t *testing.T) {
cmd := transcoder.getString()
expectedLogPath := filepath.Join("data", "logs", "transcoder.log")
expected := `FFREPORT=file="` + expectedLogPath + `":level=32 ` + transcoder.ffmpegPath + ` -hide_banner -loglevel warning -fflags +genpts -flags +cgop -i fakecontent.flv -map v:0 -c:v:0 h264_omx -b:v:0 1008k -maxrate:v:0 1088k -g:v:0 90 -keyint_min:v:0 90 -r:v:0 30 -map a:0? -c:a:0 copy -preset veryfast -map v:0 -c:v:1 h264_omx -b:v:1 3308k -maxrate:v:1 3572k -g:v:1 72 -keyint_min:v:1 72 -r:v:1 24 -map a:0? -c:a:1 copy -preset fast -map v:0 -c:v:2 copy -map a:0? -c:a:2 copy -preset ultrafast -var_stream_map "v:0,a:0 v:1,a:1 v:2,a:2 " -f hls -hls_time 3 -hls_list_size 10 -hls_flags program_date_time+independent_segments+omit_endlist -segment_format_options mpegts_flags=mpegts_copyts=1 -tune zerolatency -pix_fmt yuv420p -sc_threshold 0 -master_pl_name stream.m3u8 -hls_segment_filename http://127.0.0.1:8123/%v/stream-jdFsdfzGg-%d.ts -max_muxing_queue_size 400 -method PUT -http_persistent 1 http://127.0.0.1:8123/%v/stream.m3u8`
expected := `FFREPORT=file="` + expectedLogPath + `":level=32 ` + transcoder.ffmpegPath + ` -hide_banner -loglevel warning -fflags +genpts -flags +cgop -i fakecontent.flv -map v:0 -c:v:0 h264_omx -b:v:0 1008k -maxrate:v:0 1088k -g:v:0 90 -keyint_min:v:0 90 -r:v:0 30 -map a:0? -c:a:0 copy -preset veryfast -map v:0 -c:v:1 h264_omx -b:v:1 3308k -maxrate:v:1 3572k -g:v:1 72 -keyint_min:v:1 72 -r:v:1 24 -map a:0? -c:a:1 copy -preset fast -map v:0 -c:v:2 copy -map a:0? -c:a:2 copy -preset ultrafast -var_stream_map "v:0,a:0 v:1,a:1 v:2,a:2 " -f hls -hls_time 3 -hls_list_size 10 -hls_flags program_date_time+independent_segments+omit_endlist -segment_format_options mpegts_flags=mpegts_copyts=1 -tune zerolatency -pix_fmt yuv420p -sc_threshold 0 -master_pl_name stream.m3u8 -hls_segment_filename http://127.0.0.1:8123/%v/stream-jdFsdfzGg-%d.ts -max_muxing_queue_size 400 -method PUT http://127.0.0.1:8123/%v/stream.m3u8`
if cmd != expected {
t.Errorf("ffmpeg command does not match expected.\nGot %s\n, want: %s", cmd, expected)

View File

@@ -0,0 +1,50 @@
package transcoder
import (
"path/filepath"
"testing"
"github.com/owncast/owncast/models"
)
func TestFFmpegQuicksyncCommand(t *testing.T) {
latencyLevel := models.GetLatencyLevel(2)
codec := QuicksyncCodec{}
transcoder := new(Transcoder)
transcoder.ffmpegPath = filepath.Join("fake", "path", "ffmpeg")
transcoder.SetInput("fakecontent.flv")
transcoder.SetOutputPath("fakeOutput")
transcoder.SetIdentifier("jdofFGg")
transcoder.SetInternalHTTPPort("8123")
transcoder.SetCodec(codec.Name())
transcoder.currentLatencyLevel = latencyLevel
variant := HLSVariant{}
variant.videoBitrate = 1200
variant.isAudioPassthrough = true
variant.SetVideoFramerate(30)
variant.SetCPUUsageLevel(2)
transcoder.AddVariant(variant)
variant2 := HLSVariant{}
variant2.videoBitrate = 3500
variant2.isAudioPassthrough = true
variant2.SetVideoFramerate(24)
variant2.SetCPUUsageLevel(4)
transcoder.AddVariant(variant2)
variant3 := HLSVariant{}
variant3.isAudioPassthrough = true
variant3.isVideoPassthrough = true
transcoder.AddVariant(variant3)
cmd := transcoder.getString()
expectedLogPath := filepath.Join("data", "logs", "transcoder.log")
expected := `FFREPORT=file="` + expectedLogPath + `":level=32 ` + transcoder.ffmpegPath + ` -hide_banner -loglevel warning -init_hw_device qsv=hw -filter_hw_device hw -fflags +genpts -flags +cgop -i fakecontent.flv -map v:0 -c:v:0 h264_qsv -b:v:0 1008k -maxrate:v:0 1088k -g:v:0 90 -keyint_min:v:0 90 -r:v:0 30 -map a:0? -c:a:0 copy -filter:v:0 "hwupload=extra_hw_frames=64,format=qsv" -preset medium -map v:0 -c:v:1 h264_qsv -b:v:1 3308k -maxrate:v:1 3572k -g:v:1 72 -keyint_min:v:1 72 -r:v:1 24 -map a:0? -c:a:1 copy -filter:v:1 "hwupload=extra_hw_frames=64,format=qsv" -preset veryslow -map v:0 -c:v:2 copy -map a:0? -c:a:2 copy -preset veryfast -var_stream_map "v:0,a:0 v:1,a:1 v:2,a:2 " -f hls -hls_time 3 -hls_list_size 10 -hls_flags program_date_time+independent_segments+omit_endlist -segment_format_options mpegts_flags=mpegts_copyts=1 -pix_fmt qsv -sc_threshold 0 -master_pl_name stream.m3u8 -hls_segment_filename http://127.0.0.1:8123/%v/stream-jdofFGg-%d.ts -max_muxing_queue_size 400 -method PUT http://127.0.0.1:8123/%v/stream.m3u8`
if cmd != expected {
t.Errorf("ffmpeg command does not match expected.\nGot %s\n, want: %s", cmd, expected)
}
}

View File

@@ -42,7 +42,7 @@ func TestFFmpegVaapiCommand(t *testing.T) {
cmd := transcoder.getString()
expectedLogPath := filepath.Join("data", "logs", "transcoder.log")
expected := `FFREPORT=file="` + expectedLogPath + `":level=32 ` + transcoder.ffmpegPath + ` -hide_banner -loglevel warning -hwaccel vaapi -hwaccel_output_format vaapi -vaapi_device /dev/dri/renderD128 -fflags +genpts -flags +cgop -i fakecontent.flv -map v:0 -c:v:0 h264_vaapi -b:v:0 1008k -maxrate:v:0 1088k -g:v:0 90 -keyint_min:v:0 90 -r:v:0 30 -map a:0? -c:a:0 copy -preset veryfast -map v:0 -c:v:1 h264_vaapi -b:v:1 3308k -maxrate:v:1 3572k -g:v:1 72 -keyint_min:v:1 72 -r:v:1 24 -map a:0? -c:a:1 copy -preset fast -map v:0 -c:v:2 copy -map a:0? -c:a:2 copy -preset ultrafast -var_stream_map "v:0,a:0 v:1,a:1 v:2,a:2 " -f hls -hls_time 3 -hls_list_size 10 -hls_flags program_date_time+independent_segments+omit_endlist -segment_format_options mpegts_flags=mpegts_copyts=1 -pix_fmt vaapi_vld -sc_threshold 0 -master_pl_name stream.m3u8 -hls_segment_filename http://127.0.0.1:8123/%v/stream-jdofFGg-%d.ts -max_muxing_queue_size 400 -method PUT -http_persistent 1 http://127.0.0.1:8123/%v/stream.m3u8`
expected := `FFREPORT=file="` + expectedLogPath + `":level=32 ` + transcoder.ffmpegPath + ` -hide_banner -loglevel warning -hwaccel vaapi -hwaccel_output_format vaapi -vaapi_device /dev/dri/renderD128 -fflags +genpts -flags +cgop -i fakecontent.flv -map v:0 -c:v:0 h264_vaapi -b:v:0 1008k -maxrate:v:0 1088k -g:v:0 90 -keyint_min:v:0 90 -r:v:0 30 -map a:0? -c:a:0 copy -filter:v:0 "hwupload=extra_hw_frames=64,format=vaapi" -preset veryfast -map v:0 -c:v:1 h264_vaapi -b:v:1 3308k -maxrate:v:1 3572k -g:v:1 72 -keyint_min:v:1 72 -r:v:1 24 -map a:0? -c:a:1 copy -filter:v:1 "hwupload=extra_hw_frames=64,format=vaapi" -preset fast -map v:0 -c:v:2 copy -map a:0? -c:a:2 copy -preset ultrafast -var_stream_map "v:0,a:0 v:1,a:1 v:2,a:2 " -f hls -hls_time 3 -hls_list_size 10 -hls_flags program_date_time+independent_segments+omit_endlist -segment_format_options mpegts_flags=mpegts_copyts=1 -pix_fmt vaapi -sc_threshold 0 -master_pl_name stream.m3u8 -hls_segment_filename http://127.0.0.1:8123/%v/stream-jdofFGg-%d.ts -max_muxing_queue_size 400 -method PUT http://127.0.0.1:8123/%v/stream.m3u8`
if cmd != expected {
t.Errorf("ffmpeg command does not match expected.\nGot %s\n, want: %s", cmd, expected)

View File

@@ -42,7 +42,7 @@ func TestFFmpegVideoToolboxCommand(t *testing.T) {
cmd := transcoder.getString()
expectedLogPath := filepath.Join("data", "logs", "transcoder.log")
expected := `FFREPORT=file="` + expectedLogPath + `":level=32 ` + transcoder.ffmpegPath + ` -hide_banner -loglevel warning -fflags +genpts -flags +cgop -i fakecontent.flv -map v:0 -c:v:0 h264_videotoolbox -b:v:0 1008k -maxrate:v:0 1088k -g:v:0 90 -keyint_min:v:0 90 -r:v:0 30 -realtime true -map a:0? -c:a:0 copy -preset veryfast -map v:0 -c:v:1 h264_videotoolbox -b:v:1 3308k -maxrate:v:1 3572k -g:v:1 72 -keyint_min:v:1 72 -r:v:1 24 -map a:0? -c:a:1 copy -preset fast -map v:0 -c:v:2 copy -map a:0? -c:a:2 copy -preset ultrafast -var_stream_map "v:0,a:0 v:1,a:1 v:2,a:2 " -f hls -hls_time 3 -hls_list_size 10 -hls_flags program_date_time+independent_segments+omit_endlist -segment_format_options mpegts_flags=mpegts_copyts=1 -pix_fmt nv12 -sc_threshold 0 -master_pl_name stream.m3u8 -hls_segment_filename http://127.0.0.1:8123/%v/stream-jdFsdfzGg-%d.ts -max_muxing_queue_size 400 -method PUT -http_persistent 1 http://127.0.0.1:8123/%v/stream.m3u8`
expected := `FFREPORT=file="` + expectedLogPath + `":level=32 ` + transcoder.ffmpegPath + ` -hide_banner -loglevel warning -fflags +genpts -flags +cgop -i fakecontent.flv -map v:0 -c:v:0 h264_videotoolbox -b:v:0 1008k -maxrate:v:0 1088k -g:v:0 90 -keyint_min:v:0 90 -r:v:0 30 -realtime true -map a:0? -c:a:0 copy -preset veryfast -map v:0 -c:v:1 h264_videotoolbox -b:v:1 3308k -maxrate:v:1 3572k -g:v:1 72 -keyint_min:v:1 72 -r:v:1 24 -map a:0? -c:a:1 copy -preset fast -map v:0 -c:v:2 copy -map a:0? -c:a:2 copy -preset ultrafast -var_stream_map "v:0,a:0 v:1,a:1 v:2,a:2 " -f hls -hls_time 3 -hls_list_size 10 -hls_flags program_date_time+independent_segments+omit_endlist -segment_format_options mpegts_flags=mpegts_copyts=1 -pix_fmt nv12 -sc_threshold 0 -master_pl_name stream.m3u8 -hls_segment_filename http://127.0.0.1:8123/%v/stream-jdFsdfzGg-%d.ts -max_muxing_queue_size 400 -method PUT http://127.0.0.1:8123/%v/stream.m3u8`
if cmd != expected {
t.Errorf("ffmpeg command does not match expected.\nGot %s\n, want: %s", cmd, expected)

View File

@@ -42,7 +42,7 @@ func TestFFmpegx264Command(t *testing.T) {
cmd := transcoder.getString()
expectedLogPath := filepath.Join("data", "logs", "transcoder.log")
expected := `FFREPORT=file="` + expectedLogPath + `":level=32 ` + transcoder.ffmpegPath + ` -hide_banner -loglevel warning -fflags +genpts -flags +cgop -i fakecontent.flv -map v:0 -c:v:0 libx264 -b:v:0 1008k -maxrate:v:0 1088k -g:v:0 90 -keyint_min:v:0 90 -r:v:0 30 -x264-params:v:0 "scenecut=0:open_gop=0" -bufsize:v:0 1088k -profile:v:0 high -map a:0? -c:a:0 copy -preset veryfast -map v:0 -c:v:1 libx264 -b:v:1 3308k -maxrate:v:1 3572k -g:v:1 72 -keyint_min:v:1 72 -r:v:1 24 -x264-params:v:1 "scenecut=0:open_gop=0" -bufsize:v:1 3572k -profile:v:1 high -map a:0? -c:a:1 copy -preset fast -map v:0 -c:v:2 copy -map a:0? -c:a:2 copy -preset ultrafast -var_stream_map "v:0,a:0 v:1,a:1 v:2,a:2 " -f hls -hls_time 3 -hls_list_size 10 -hls_flags program_date_time+independent_segments+omit_endlist -segment_format_options mpegts_flags=mpegts_copyts=1 -tune zerolatency -pix_fmt yuv420p -sc_threshold 0 -master_pl_name stream.m3u8 -hls_segment_filename http://127.0.0.1:8123/%v/stream-jdofFGg-%d.ts -max_muxing_queue_size 400 -method PUT -http_persistent 1 http://127.0.0.1:8123/%v/stream.m3u8`
expected := `FFREPORT=file="` + expectedLogPath + `":level=32 ` + transcoder.ffmpegPath + ` -hide_banner -loglevel warning -fflags +genpts -flags +cgop -i fakecontent.flv -map v:0 -c:v:0 libx264 -b:v:0 1008k -maxrate:v:0 1088k -g:v:0 90 -keyint_min:v:0 90 -r:v:0 30 -x264-params:v:0 "scenecut=0:open_gop=0" -bufsize:v:0 1088k -profile:v:0 high -map a:0? -c:a:0 copy -preset veryfast -map v:0 -c:v:1 libx264 -b:v:1 3308k -maxrate:v:1 3572k -g:v:1 72 -keyint_min:v:1 72 -r:v:1 24 -x264-params:v:1 "scenecut=0:open_gop=0" -bufsize:v:1 3572k -profile:v:1 high -map a:0? -c:a:1 copy -preset fast -map v:0 -c:v:2 copy -map a:0? -c:a:2 copy -preset ultrafast -var_stream_map "v:0,a:0 v:1,a:1 v:2,a:2 " -f hls -hls_time 3 -hls_list_size 10 -hls_flags program_date_time+independent_segments+omit_endlist -segment_format_options mpegts_flags=mpegts_copyts=1 -tune zerolatency -pix_fmt yuv420p -sc_threshold 0 -master_pl_name stream.m3u8 -hls_segment_filename http://127.0.0.1:8123/%v/stream-jdofFGg-%d.ts -max_muxing_queue_size 400 -method PUT http://127.0.0.1:8123/%v/stream.m3u8`
if cmd != expected {
t.Errorf("ffmpeg command does not match expected.\nGot %s\n, want: %s", cmd, expected)

View File

@@ -8,7 +8,7 @@ import (
"sync"
"github.com/owncast/owncast/config"
"github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/persistence/configrepository"
"github.com/owncast/owncast/utils"
log "github.com/sirupsen/logrus"
)
@@ -59,6 +59,8 @@ var ignoredErrors = []string{
"Non-monotonous DTS in output",
"frames duplicated",
"To ignore this",
"Driver does not support some wanted packed headers (wanted 0xd, found 0x1)",
"Failed to allocate a vaapi/nv12 frame from a fixed pool of hardware frames.",
}
func handleTranscoderMessage(message string) {
@@ -99,9 +101,9 @@ func handleTranscoderMessage(message string) {
func createVariantDirectories() {
// Create private hls data dirs
utils.CleanupDirectory(config.HLSStoragePath)
if len(data.GetStreamOutputVariants()) != 0 {
for index := range data.GetStreamOutputVariants() {
configRepository := configrepository.Get()
if len(configRepository.GetStreamOutputVariants()) != 0 {
for index := range configRepository.GetStreamOutputVariants() {
if err := os.MkdirAll(path.Join(config.HLSStoragePath, strconv.Itoa(index)), 0o750); err != nil {
log.Fatalln(err)
}

View File

@@ -3,8 +3,8 @@ package webhooks
import (
"time"
"github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/models"
"github.com/owncast/owncast/persistence/configrepository"
"github.com/teris-io/shortid"
)
@@ -14,13 +14,15 @@ func SendStreamStatusEvent(eventType models.EventType) {
}
func sendStreamStatusEvent(eventType models.EventType, id string, timestamp time.Time) {
configRepository := configrepository.Get()
SendEventToWebhooks(WebhookEvent{
Type: eventType,
EventData: map[string]interface{}{
"id": id,
"name": data.GetServerName(),
"summary": data.GetServerSummary(),
"streamTitle": data.GetStreamTitle(),
"name": configRepository.GetServerName(),
"summary": configRepository.GetServerSummary(),
"streamTitle": configRepository.GetStreamTitle(),
"status": getStatus(),
"timestamp": timestamp,
},

View File

@@ -5,14 +5,16 @@ import (
"time"
"github.com/owncast/owncast/core/chat/events"
"github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/models"
"github.com/owncast/owncast/persistence/configrepository"
)
func TestSendStreamStatusEvent(t *testing.T) {
data.SetServerName("my server")
data.SetServerSummary("my server where I stream")
data.SetStreamTitle("my stream")
configRepository := configrepository.Get()
configRepository.SetServerName("my server")
configRepository.SetServerSummary("my server where I stream")
configRepository.SetStreamTitle("my stream")
checkPayload(t, models.StreamStarted, func() {
sendStreamStatusEvent(events.StreamStarted, "id", time.Unix(72, 6).UTC())

View File

@@ -4,8 +4,8 @@ import (
"sync"
"time"
"github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/models"
"github.com/owncast/owncast/persistence/webhookrepository"
)
// WebhookEvent represents an event sent as a webhook.
@@ -31,7 +31,8 @@ func SendEventToWebhooks(payload WebhookEvent) {
}
func sendEventToWebhooks(payload WebhookEvent, wg *sync.WaitGroup) {
webhooks := data.GetWebhooksForEvent(payload.Type)
webhooksRepo := webhookrepository.Get()
webhooks := webhooksRepo.GetWebhooksForEvent(payload.Type)
for _, webhook := range webhooks {
// Use wg to track the number of notifications to be sent.

View File

@@ -15,6 +15,7 @@ import (
"github.com/owncast/owncast/core/chat/events"
"github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/models"
"github.com/owncast/owncast/persistence/webhookrepository"
jsonpatch "gopkg.in/evanphx/json-patch.v5"
)
@@ -62,12 +63,14 @@ func TestPublicSend(t *testing.T) {
}))
defer svr.Close()
hook, err := data.InsertWebhook(svr.URL, []models.EventType{models.MessageSent})
webhooksRepo := webhookrepository.Get()
hook, err := webhooksRepo.InsertWebhook(svr.URL, []models.EventType{models.MessageSent})
if err != nil {
t.Fatal(err)
}
defer func() {
if err := data.DeleteWebhook(hook); err != nil {
if err := webhooksRepo.DeleteWebhook(hook); err != nil {
t.Error(err)
}
}()
@@ -107,13 +110,15 @@ func TestRouting(t *testing.T) {
}))
defer svr.Close()
webhooksRepo := webhookrepository.Get()
for _, eventType := range eventTypes {
hook, err := data.InsertWebhook(svr.URL+"/"+eventType, []models.EventType{eventType})
hook, err := webhooksRepo.InsertWebhook(svr.URL+"/"+eventType, []models.EventType{eventType})
if err != nil {
t.Fatal(err)
}
defer func() {
if err := data.DeleteWebhook(hook); err != nil {
if err := webhooksRepo.DeleteWebhook(hook); err != nil {
t.Error(err)
}
}()
@@ -148,13 +153,15 @@ func TestMultiple(t *testing.T) {
}))
defer svr.Close()
webhooksRepo := webhookrepository.Get()
for i := 0; i < times; i++ {
hook, err := data.InsertWebhook(fmt.Sprintf("%v/%v", svr.URL, i), []models.EventType{models.MessageSent})
hook, err := webhooksRepo.InsertWebhook(fmt.Sprintf("%v/%v", svr.URL, i), []models.EventType{models.MessageSent})
if err != nil {
t.Fatal(err)
}
defer func() {
if err := data.DeleteWebhook(hook); err != nil {
if err := webhooksRepo.DeleteWebhook(hook); err != nil {
t.Error(err)
}
}()
@@ -186,14 +193,16 @@ func TestTimestamps(t *testing.T) {
}))
defer svr.Close()
webhooksRepo := webhookrepository.Get()
for i, eventType := range eventTypes {
hook, err := data.InsertWebhook(svr.URL+"/"+eventType, []models.EventType{eventType})
hook, err := webhooksRepo.InsertWebhook(svr.URL+"/"+eventType, []models.EventType{eventType})
if err != nil {
t.Fatal(err)
}
handlerIds[i] = hook
defer func() {
if err := data.DeleteWebhook(hook); err != nil {
if err := webhooksRepo.DeleteWebhook(hook); err != nil {
t.Error(err)
}
}()
@@ -209,7 +218,7 @@ func TestTimestamps(t *testing.T) {
wg.Wait()
hooks, err := data.GetWebhooks()
hooks, err := webhooksRepo.GetWebhooks()
if err != nil {
t.Fatal(err)
}
@@ -285,12 +294,14 @@ func TestParallel(t *testing.T) {
}))
defer svr.Close()
hook, err := data.InsertWebhook(svr.URL, []models.EventType{models.MessageSent})
webhooksRepo := webhookrepository.Get()
hook, err := webhooksRepo.InsertWebhook(svr.URL, []models.EventType{models.MessageSent})
if err != nil {
t.Fatal(err)
}
defer func() {
if err := data.DeleteWebhook(hook); err != nil {
if err := webhooksRepo.DeleteWebhook(hook); err != nil {
t.Error(err)
}
}()
@@ -320,13 +331,15 @@ func checkPayload(t *testing.T, eventType models.EventType, send func(), expecte
}))
defer svr.Close()
webhooksRepo := webhookrepository.Get()
// Subscribe to the webhook.
hook, err := data.InsertWebhook(svr.URL, []models.EventType{eventType})
hook, err := webhooksRepo.InsertWebhook(svr.URL, []models.EventType{eventType})
if err != nil {
t.Fatal(err)
}
defer func() {
if err := data.DeleteWebhook(hook); err != nil {
if err := webhooksRepo.DeleteWebhook(hook); err != nil {
t.Error(err)
}
}()

View File

@@ -9,8 +9,8 @@ import (
log "github.com/sirupsen/logrus"
"github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/models"
"github.com/owncast/owncast/persistence/webhookrepository"
)
// webhookWorkerPoolSize defines the number of concurrent HTTP webhook requests.
@@ -87,7 +87,8 @@ func sendWebhook(job Job) error {
defer resp.Body.Close()
if err := data.SetWebhookAsUsed(job.webhook); err != nil {
webhooksRepo := webhookrepository.Get()
if err := webhooksRepo.SetWebhookAsUsed(job.webhook); err != nil {
log.Warnln(err)
}

11
crowdin.yml Normal file
View File

@@ -0,0 +1,11 @@
project_id_env: CROWDIN_PROJECT_ID
api_token_env: CROWDIN_PERSONAL_TOKEN
pull_request_title: Translations update
pull_request_labels:
- crowdin
- i18n
- translation
commit_message: 'Updated translations for %language%'
files:
- source: /web/i18n/en/translation.json
translation: /web/i18n/%two_letters_code%/translation.json

File diff suppressed because one or more lines are too long

40
go.mod
View File

@@ -6,15 +6,15 @@ toolchain go1.23.1
require (
github.com/CAFxX/httpcompression v0.0.9
github.com/SherClockHolmes/webpush-go v1.3.0
github.com/TwiN/go-away v1.6.13
github.com/andybalholm/cascadia v1.3.2
github.com/aws/aws-sdk-go v1.55.5
github.com/go-chi/chi/v5 v5.1.0
github.com/go-fed/activity v1.0.1-0.20210803212804-d866ba75dd0f
github.com/SherClockHolmes/webpush-go v1.4.0
github.com/TwiN/go-away v1.6.14
github.com/andybalholm/cascadia v1.3.3
github.com/aws/aws-sdk-go v1.55.6
github.com/go-chi/chi/v5 v5.2.1
github.com/go-fed/activity v1.0.1-0.20220119073622-b14b50eecad0
github.com/go-fed/httpsig v1.1.0
github.com/gorilla/websocket v1.5.3
github.com/grafov/m3u8 v0.12.0
github.com/grafov/m3u8 v0.12.1
github.com/jellydator/ttlcache/v3 v3.3.0
github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible
github.com/mattn/go-sqlite3 v1.14.24
@@ -28,18 +28,18 @@ require (
github.com/prometheus/client_golang v1.20.5
github.com/rifflock/lfshook v0.0.0-20180920164130-b9218ef580f5
github.com/schollz/sqlite3dump v1.3.1
github.com/shirou/gopsutil/v4 v4.24.9
github.com/shirou/gopsutil/v4 v4.25.1
github.com/sirupsen/logrus v1.9.3
github.com/stretchr/testify v1.9.0
github.com/stretchr/testify v1.10.0
github.com/teris-io/shortid v0.0.0-20220617161101-71ec9f2aa569
github.com/yuin/goldmark v1.7.8
github.com/yuin/goldmark-emoji v1.0.4
golang.org/x/crypto v0.28.0
golang.org/x/mod v0.21.0
golang.org/x/net v0.30.0
golang.org/x/time v0.7.0
gopkg.in/evanphx/json-patch.v5 v5.9.0
mvdan.cc/xurls/v2 v2.5.0
golang.org/x/crypto v0.33.0
golang.org/x/mod v0.23.0
golang.org/x/net v0.35.0
golang.org/x/time v0.10.0
gopkg.in/evanphx/json-patch.v5 v5.9.11
mvdan.cc/xurls/v2 v2.6.0
)
require (
@@ -49,10 +49,10 @@ require (
github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/ebitengine/purego v0.8.0 // indirect
github.com/ebitengine/purego v0.8.2 // indirect
github.com/go-ole/go-ole v1.2.6 // indirect
github.com/go-test/deep v1.0.4 // indirect
github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
github.com/golang-jwt/jwt/v5 v5.2.1 // indirect
github.com/google/uuid v1.5.0 // indirect
github.com/gorilla/css v1.0.1 // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect
@@ -70,9 +70,9 @@ require (
github.com/tklauser/go-sysconf v0.3.12 // indirect
github.com/tklauser/numcpus v0.6.1 // indirect
github.com/yusufpapurcu/wmi v1.2.4 // indirect
golang.org/x/sync v0.8.0 // indirect
golang.org/x/sys v0.26.0 // indirect
golang.org/x/text v0.19.0 // indirect
golang.org/x/sync v0.11.0 // indirect
golang.org/x/sys v0.30.0 // indirect
golang.org/x/text v0.22.0 // indirect
google.golang.org/protobuf v1.34.2 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

138
go.sum
View File

@@ -1,18 +1,18 @@
github.com/CAFxX/httpcompression v0.0.9 h1:0ue2X8dOLEpxTm8tt+OdHcgA+gbDge0OqFQWGKSqgrg=
github.com/CAFxX/httpcompression v0.0.9/go.mod h1:XX8oPZA+4IDcfZ0A71Hz0mZsv/YJOgYygkFhizVPilM=
github.com/RaveNoX/go-jsoncommentstrip v1.0.0/go.mod h1:78ihd09MekBnJnxpICcwzCMzGrKSKYe4AqU6PDYYpjk=
github.com/SherClockHolmes/webpush-go v1.3.0 h1:CAu3FvEE9QS4drc3iKNgpBWFfGqNthKlZhp5QpYnu6k=
github.com/SherClockHolmes/webpush-go v1.3.0/go.mod h1:AxRHmJuYwKGG1PVgYzToik1lphQvDnqFYDqimHvwhIw=
github.com/TwiN/go-away v1.6.13 h1:aB6l/FPXmA5ds+V7I9zdhxzpsLLUvVtEuS++iU/ZmgE=
github.com/TwiN/go-away v1.6.13/go.mod h1:MpvIC9Li3minq+CGgbgUDvQ9tDaeW35k5IXZrF9MVas=
github.com/SherClockHolmes/webpush-go v1.4.0 h1:ocnzNKWN23T9nvHi6IfyrQjkIc0oJWv1B1pULsf9i3s=
github.com/SherClockHolmes/webpush-go v1.4.0/go.mod h1:XSq8pKX11vNV8MJEMwjrlTkxhAj1zKfxmyhdV7Pd6UA=
github.com/TwiN/go-away v1.6.14 h1:gjFP+6/A36gmj0NpYX0Sz9hrdU0KtHwtNWYnsJgV4fo=
github.com/TwiN/go-away v1.6.14/go.mod h1:d+Gv3XuqjIeFqXYuAIzlyNoDzr1vNsP5B/hRY3u/VLs=
github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs=
github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
github.com/andybalholm/cascadia v1.3.2 h1:3Xi6Dw5lHF15JtdcmAHD3i1+T8plmv7BQ/nsViSLyss=
github.com/andybalholm/cascadia v1.3.2/go.mod h1:7gtRlve5FxPPgIgX36uWBX58OdBsSS6lUvCFb+h7KvU=
github.com/andybalholm/cascadia v1.3.3 h1:AG2YHrzJIm4BZ19iwJ/DAua6Btl3IwJX+VI4kktS1LM=
github.com/andybalholm/cascadia v1.3.3/go.mod h1:xNd9bqTn98Ln4DwST8/nG+H0yuB8Hmgu1YHNnWw0GeA=
github.com/apapsch/go-jsonmerge/v2 v2.0.0 h1:axGnT1gRIfimI7gJifB699GoE/oq+F2MU7Dml6nw9rQ=
github.com/apapsch/go-jsonmerge/v2 v2.0.0/go.mod h1:lvDnEdqiQrp0O42VQGgmlKpxL1AP2+08jFMw88y4klk=
github.com/aws/aws-sdk-go v1.55.5 h1:KKUZBfBoyqy5d3swXyiC7Q76ic40rYcbqH7qjh59kzU=
github.com/aws/aws-sdk-go v1.55.5/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU=
github.com/aws/aws-sdk-go v1.55.6 h1:cSg4pvZ3m8dgYcgqB97MrcdjUmZ1BeMYKUxMMB89IPk=
github.com/aws/aws-sdk-go v1.55.6/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU=
github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=
github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
@@ -24,10 +24,10 @@ github.com/dave/jennifer v1.3.0/go.mod h1:fIb+770HOpJ2fmN9EPPKOqm1vMGhB+TwXKMZhr
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/ebitengine/purego v0.8.0 h1:JbqvnEzRvPpxhCJzJJ2y0RbiZ8nyjccVUrSM3q+GvvE=
github.com/ebitengine/purego v0.8.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
github.com/go-chi/chi/v5 v5.1.0 h1:acVI1TYaD+hhedDJ3r54HyA6sExp3HfXq7QWEEY/xMw=
github.com/go-chi/chi/v5 v5.1.0/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
github.com/ebitengine/purego v0.8.2 h1:jPPGWs2sZ1UgOSgD2bClL0MJIqu58nOmIcBuXr62z1I=
github.com/ebitengine/purego v0.8.2/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
github.com/go-chi/chi/v5 v5.2.1 h1:KOIHODQj58PmL80G2Eak4WdvUzjSJSm0vG72crDCqb8=
github.com/go-chi/chi/v5 v5.2.1/go.mod h1:L2yAIGWB3H+phAw1NxKwWM+7eUH/lU8pOMm5hHcoops=
github.com/go-fed/httpsig v0.1.1-0.20190914113940-c2de3672e5b5/go.mod h1:T56HUNYZUQ1AGUzhAYPugZfp36sKApVnGBgKlIY+aIE=
github.com/go-fed/httpsig v1.1.0 h1:9M+hb0jkEICD8/cAiNqEB66R87tTINszBRTjwjQzWcI=
github.com/go-fed/httpsig v1.1.0/go.mod h1:RCMrTZvN1bJYtofsG4rd5NaO5obxQ5xBkdiS7xsT7bM=
@@ -36,8 +36,8 @@ github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiU
github.com/go-test/deep v1.0.1/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
github.com/go-test/deep v1.0.4 h1:u2CU3YKy9I2pmu9pX0eq50wCgjfGIt539SqR7FbHiho=
github.com/go-test/deep v1.0.4/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk=
github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/google/brotli/go/cbrotli v0.0.0-20230829110029-ed738e842d2f h1:jopqB+UTSdJGEJT8tEqYyE29zN91fi2827oLET8tl7k=
github.com/google/brotli/go/cbrotli v0.0.0-20230829110029-ed738e842d2f/go.mod h1:nOPhAkwVliJdNTkj3gXpljmWhjc4wCaVqbMJcPKWP4s=
@@ -50,8 +50,8 @@ github.com/gorilla/css v1.0.1 h1:ntNaBIghp6JmvWnxbZKANoLyuXTPZ4cAMlo6RyhlbO8=
github.com/gorilla/css v1.0.1/go.mod h1:BvnYkspnSzMmwRK+b8/xgNPLiIuNZr6vbZBTPQ2A3b0=
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/grafov/m3u8 v0.12.0 h1:T6iTwTsSEtMcwkayef+FJO8kj+Sglr4Lh81Zj8Ked/4=
github.com/grafov/m3u8 v0.12.0/go.mod h1:nqzOkfBiZJENr52zTVd/Dcl03yzphIMbJqkXGu+u080=
github.com/grafov/m3u8 v0.12.1 h1:DuP1uA1kvRRmGNAZ0m+ObLv1dvrfNO0TPx0c/enNk0s=
github.com/grafov/m3u8 v0.12.1/go.mod h1:nqzOkfBiZJENr52zTVd/Dcl03yzphIMbJqkXGu+u080=
github.com/jellydator/ttlcache/v3 v3.3.0 h1:BdoC9cE81qXfrxeb9eoJi9dWrdhSuwXMAnHTbnBm4Wc=
github.com/jellydator/ttlcache/v3 v3.3.0/go.mod h1:bj2/e0l4jRnQdrnSTaGTsh4GSXvMjQcy41i7th0GVGw=
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
@@ -80,8 +80,6 @@ github.com/lestrrat-go/strftime v1.0.4/go.mod h1:E1nN3pCbtMSu1yjSVeyuRFVm/U0xoR7
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4=
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
github.com/mattn/go-sqlite3 v1.14.7/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/mattn/go-sqlite3 v1.14.24 h1:tpSp2G2KyMnnQu99ngJ47EIkWVmliIizyZBfPrBWDRM=
github.com/mattn/go-sqlite3 v1.14.24/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/microcosm-cc/bluemonday v1.0.27 h1:MpEUotklkwCSLeH+Qdx1VJgNqLlpY2KXwXFM08ygZfk=
@@ -111,8 +109,6 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw=
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
github.com/prometheus/client_golang v1.20.0 h1:jBzTZ7B099Rg24tny+qngoynol8LtVYlA2bqx3vEloI=
github.com/prometheus/client_golang v1.20.0/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y=
github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
@@ -123,12 +119,12 @@ github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0leargg
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
github.com/rifflock/lfshook v0.0.0-20180920164130-b9218ef580f5 h1:mZHayPoR0lNmnHyvtYjDeq0zlVHn9K/ZXoy17ylucdo=
github.com/rifflock/lfshook v0.0.0-20180920164130-b9218ef580f5/go.mod h1:GEXHk5HgEKCvEIIrSpFI3ozzG5xOKA2DVlEX/gGnewM=
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
github.com/rogpeppe/go-internal v1.13.2-0.20241226121412-a5dc8ff20d0a h1:w3tdWGKbLGBPtR/8/oO74W6hmz0qE5q0z9aqSAewaaM=
github.com/rogpeppe/go-internal v1.13.2-0.20241226121412-a5dc8ff20d0a/go.mod h1:S8kfXMp+yh77OxPD4fdM6YUknrZpQxLhvxzS4gDHENY=
github.com/schollz/sqlite3dump v1.3.1 h1:QXizJ7XEJ7hggjqjZ3YRtF3+javm8zKtzNByYtEkPRA=
github.com/schollz/sqlite3dump v1.3.1/go.mod h1:mzSTjZpJH4zAb1FN3iNlhWPbbdyeBpOaTW0hukyMHyI=
github.com/shirou/gopsutil/v4 v4.24.9 h1:KIV+/HaHD5ka5f570RZq+2SaeFsb/pq+fp2DGNWYoOI=
github.com/shirou/gopsutil/v4 v4.24.9/go.mod h1:3fkaHNeYsUFCGZ8+9vZVWtbyM1k2eRnlL+bWO8Bxa/Q=
github.com/shirou/gopsutil/v4 v4.25.1 h1:QSWkTc+fu9LTAWfkZwZ6j8MSUk4A2LV7rbH0ZqmLjXs=
github.com/shirou/gopsutil/v4 v4.25.1/go.mod h1:RoUCUpndaJFtT+2zsZzzmhvbfGoDCJ7nFXKJf8GqJbI=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/spf13/cobra v0.0.4-0.20190109003409-7547e83b2d85/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
@@ -143,8 +139,8 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/teris-io/shortid v0.0.0-20220617161101-71ec9f2aa569 h1:xzABM9let0HLLqFypcxvLmlvEciCHL7+Lv+4vwZqecI=
github.com/teris-io/shortid v0.0.0-20220617161101-71ec9f2aa569/go.mod h1:2Ly+NIftZN4de9zRmENdYbvPQeaVIYKWpLFStLFEBgI=
github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU=
@@ -156,14 +152,8 @@ github.com/valyala/gozstd v1.20.1 h1:xPnnnvjmaDDitMFfDxmQ4vpx0+3CdTg2o3lALvXTU/g
github.com/valyala/gozstd v1.20.1/go.mod h1:y5Ew47GLlP37EkTB+B4s7r6A5rdaeB7ftbl9zoYiIPQ=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/yuin/goldmark v1.7.1/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E=
github.com/yuin/goldmark v1.7.4 h1:BDXOHExt+A7gwPCJgPIIq7ENvceR7we7rOS9TNoLZeg=
github.com/yuin/goldmark v1.7.4/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E=
github.com/yuin/goldmark v1.7.7 h1:5m9rrB1sW3JUMToKFQfb+FGt1U7r57IHu5GrYrG2nqU=
github.com/yuin/goldmark v1.7.7/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E=
github.com/yuin/goldmark v1.7.8 h1:iERMLn0/QJeHFhxSt3p6PeN9mGnvIKSpG9YYorDMnic=
github.com/yuin/goldmark v1.7.8/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E=
github.com/yuin/goldmark-emoji v1.0.3 h1:aLRkLHOuBR2czCY4R8olwMjID+tENfhyFDMCRhbIQY4=
github.com/yuin/goldmark-emoji v1.0.3/go.mod h1:tTkZEbwu5wkPmgTcitqddVxY9osFZiavD+r4AzQrh1U=
github.com/yuin/goldmark-emoji v1.0.4 h1:vCwMkPZSNefSUnOW2ZKRUjBSD5Ok3W78IXhGxxAEF90=
github.com/yuin/goldmark-emoji v1.0.4/go.mod h1:tTkZEbwu5wkPmgTcitqddVxY9osFZiavD+r4AzQrh1U=
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
@@ -174,32 +164,41 @@ golang.org/x/crypto v0.0.0-20180527072434-ab813273cd59/go.mod h1:6SG95UA2DQfeDnf
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0=
golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A=
golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70=
golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw=
golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U=
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus=
golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0=
golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.23.0 h1:Zb7khfcRGKk+kqfxFaP5tZqCnDZMjC5VtUBs87Hr6QM=
golang.org/x/mod v0.23.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo=
golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0=
golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4=
golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU=
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8=
golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w=
golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20180525142821-c11f84a56e43/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -211,37 +210,44 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY=
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/term v0.24.0 h1:Mh5cbb+Zk2hqqXNO7S1iTjEphVL+jb8ZWaqh/g+JWkM=
golang.org/x/term v0.24.0/go.mod h1:lOBK/LVxemqiMij05LGJ0tzNr8xlmwBRJ81PX6wVLH8=
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
golang.org/x/term v0.29.0 h1:L6pJp37ocefwRRtYPKSWOWzOtWSxVajvz2ldH/xi3iU=
golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224=
golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM=
golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U=
golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ=
golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
golang.org/x/time v0.10.0 h1:3usCWA8tQn0L8+hFJQNgzpWbd89begxN66o1Ojdn5L4=
golang.org/x/time v0.10.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
@@ -249,13 +255,13 @@ google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWn
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/evanphx/json-patch.v5 v5.9.0 h1:hx1VU2SGj4F8r9b8GUwJLdc8DNO8sy79ZGui0G05GLo=
gopkg.in/evanphx/json-patch.v5 v5.9.0/go.mod h1:/kvTRh1TVm5wuM6OkHxqXtE/1nUZZpihg29RtuIyfvk=
gopkg.in/evanphx/json-patch.v5 v5.9.11 h1:OMPeiLomOQwe8+Ku4nwXsdOmrRw2vGUpP3XgLj3ojNw=
gopkg.in/evanphx/json-patch.v5 v5.9.11/go.mod h1:/kvTRh1TVm5wuM6OkHxqXtE/1nUZZpihg29RtuIyfvk=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
mvdan.cc/xurls/v2 v2.5.0 h1:lyBNOm8Wo71UknhUs4QTFUNNMyxy2JEIaKKo0RWOh+8=
mvdan.cc/xurls/v2 v2.5.0/go.mod h1:yQgaGQ1rFtJUzkmKiHYSSfuQxqfYmd//X6PxvholpeE=
mvdan.cc/xurls/v2 v2.6.0 h1:3NTZpeTxYVWNSokW3MKeyVkz/j7uYXYiMtXRUfmjbgI=
mvdan.cc/xurls/v2 v2.6.0/go.mod h1:bCvEZ1XvdA6wDnxY7jPPjEmigDtvtvPXAD/Exa9IMSk=

15
main.go
View File

@@ -6,6 +6,7 @@ import (
"strconv"
"github.com/owncast/owncast/logging"
"github.com/owncast/owncast/persistence/configrepository"
log "github.com/sirupsen/logrus"
"github.com/owncast/owncast/config"
@@ -111,8 +112,10 @@ func main() {
}
func handleCommandLineFlags() {
configRepository := configrepository.Get()
if *newAdminPassword != "" {
if err := data.SetAdminPassword(*newAdminPassword); err != nil {
if err := configRepository.SetAdminPassword(*newAdminPassword); err != nil {
log.Errorln("Error setting your admin password.", err)
log.Exit(1)
} else {
@@ -134,25 +137,25 @@ func handleCommandLineFlags() {
}
log.Println("Saving new web server port number to", portNumber)
if err := data.SetHTTPPortNumber(float64(portNumber)); err != nil {
if err := configRepository.SetHTTPPortNumber(float64(portNumber)); err != nil {
log.Errorln(err)
}
}
config.WebServerPort = data.GetHTTPPortNumber()
config.WebServerPort = configRepository.GetHTTPPortNumber()
// Set the web server ip
if *webServerIPOverride != "" {
log.Println("Saving new web server listen IP address to", *webServerIPOverride)
if err := data.SetHTTPListenAddress(*webServerIPOverride); err != nil {
if err := configRepository.SetHTTPListenAddress(*webServerIPOverride); err != nil {
log.Errorln(err)
}
}
config.WebServerIP = data.GetHTTPListenAddress()
config.WebServerIP = configRepository.GetHTTPListenAddress()
// Set the rtmp server port
if *rtmpPortOverride > 0 {
log.Println("Saving new RTMP server port number to", *rtmpPortOverride)
if err := data.SetRTMPPortNumber(float64(*rtmpPortOverride)); err != nil {
if err := configRepository.SetRTMPPortNumber(float64(*rtmpPortOverride)); err != nil {
log.Errorln(err)
}
}

View File

@@ -5,8 +5,8 @@ import (
"sort"
"github.com/owncast/owncast/core"
"github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/models"
"github.com/owncast/owncast/persistence/configrepository"
"github.com/owncast/owncast/utils"
)
@@ -68,8 +68,8 @@ func networkSpeedHealthOverviewMessage() string {
isVideoPassthrough bool
bitrate int
}
outputVariants := data.GetStreamOutputVariants()
configRepository := configrepository.Get()
outputVariants := configRepository.GetStreamOutputVariants()
streamSortVariants := make([]singleVariant, len(outputVariants))
for i, variant := range outputVariants {
@@ -155,7 +155,8 @@ func wastefulBitrateOverviewMessage() string {
return ""
}
outputVariants := data.GetStreamOutputVariants()
configRepository := configrepository.Get()
outputVariants := configRepository.GetStreamOutputVariants()
type singleVariant struct {
isVideoPassthrough bool
@@ -229,7 +230,8 @@ func errorCountHealthOverviewMessage() string {
healthyPercentage := utils.IntPercentage(clientsWithErrors, totalNumberOfClients)
isUsingPassthrough := false
outputVariants := data.GetStreamOutputVariants()
configRepository := configrepository.Get()
outputVariants := configRepository.GetStreamOutputVariants()
for _, variant := range outputVariants {
if variant.IsVideoPassthrough {
isUsingPassthrough = true

View File

@@ -5,8 +5,8 @@ import (
"time"
"github.com/owncast/owncast/config"
"github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/models"
"github.com/owncast/owncast/persistence/configrepository"
)
// How often we poll for updates.
@@ -56,8 +56,9 @@ var _getStatus func() models.Status
// Start will begin the metrics collection and alerting.
func Start(getStatus func() models.Status) {
configRepository := configrepository.Get()
_getStatus = getStatus
host := data.GetServerURL()
host := configRepository.GetServerURL()
if host == "" {
host = "unknown"
}

View File

@@ -6,7 +6,7 @@ import (
"github.com/nakabonne/tstorage"
"github.com/owncast/owncast/core"
"github.com/owncast/owncast/core/chat"
"github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/persistence/chatmessagerepository"
"github.com/owncast/owncast/persistence/userrepository"
log "github.com/sirupsen/logrus"
)
@@ -56,7 +56,8 @@ func collectChatClientCount() {
activeChatClientCount.Set(float64(count))
// Total message count
cmc := data.GetMessagesCount()
chatMessageRepository := chatmessagerepository.Get()
cmc := chatMessageRepository.GetMessagesCount()
// Insert message count into Prometheus collector.
currentChatMessageCount.Set(float64(cmc))

View File

@@ -1,4 +1,4 @@
package data
package models
import (
"bytes"
@@ -12,48 +12,48 @@ type ConfigEntry struct {
Key string
}
func (c *ConfigEntry) getStringSlice() ([]string, error) {
decoder := c.getDecoder()
func (c *ConfigEntry) GetStringSlice() ([]string, error) {
decoder := c.GetDecoder()
var result []string
err := decoder.Decode(&result)
return result, err
}
func (c *ConfigEntry) getStringMap() (map[string]string, error) {
decoder := c.getDecoder()
func (c *ConfigEntry) GetStringMap() (map[string]string, error) {
decoder := c.GetDecoder()
var result map[string]string
err := decoder.Decode(&result)
return result, err
}
func (c *ConfigEntry) getString() (string, error) {
decoder := c.getDecoder()
func (c *ConfigEntry) GetString() (string, error) {
decoder := c.GetDecoder()
var result string
err := decoder.Decode(&result)
return result, err
}
func (c *ConfigEntry) getNumber() (float64, error) {
decoder := c.getDecoder()
func (c *ConfigEntry) GetNumber() (float64, error) {
decoder := c.GetDecoder()
var result float64
err := decoder.Decode(&result)
return result, err
}
func (c *ConfigEntry) getBool() (bool, error) {
decoder := c.getDecoder()
func (c *ConfigEntry) GetBool() (bool, error) {
decoder := c.GetDecoder()
var result bool
err := decoder.Decode(&result)
return result, err
}
func (c *ConfigEntry) getObject(result interface{}) error {
decoder := c.getDecoder()
func (c *ConfigEntry) GetObject(result interface{}) error {
decoder := c.GetDecoder()
err := decoder.Decode(result)
return err
}
func (c *ConfigEntry) getDecoder() *gob.Decoder {
func (c *ConfigEntry) GetDecoder() *gob.Decoder {
valueBytes := c.Value.([]byte)
decoder := gob.NewDecoder(bytes.NewBuffer(valueBytes))
return decoder

View File

@@ -1,7 +0,0 @@
package models
// CustomEmoji represents an image that can be used in chat as a custom emoji.
type CustomEmoji struct {
Name string `json:"name"`
URL string `json:"url"`
}

View File

@@ -1,10 +0,0 @@
package models
import "time"
// IPAddress is a simple representation of an IP address.
type IPAddress struct {
CreatedAt time.Time `json:"createdAt"`
IPAddress string `json:"ipAddress"`
Notes string `json:"notes"`
}

View File

@@ -1,7 +0,0 @@
package models
// StreamKey represents a single stream key.
type StreamKey struct {
Key string `json:"key"`
Comment string `json:"comment"`
}

View File

@@ -8,6 +8,7 @@ import (
"github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/db"
"github.com/owncast/owncast/models"
"github.com/owncast/owncast/persistence/configrepository"
"github.com/owncast/owncast/persistence/tables"
"github.com/owncast/owncast/notifications/browser"
@@ -18,9 +19,10 @@ import (
// Notifier is an instance of the live stream notifier.
type Notifier struct {
datastore *data.Datastore
browser *browser.Browser
discord *discord.Discord
datastore *data.Datastore
browser *browser.Browser
discord *discord.Discord
configRepository configrepository.ConfigRepository
}
// Setup will perform any pre-use setup for the notifier.
@@ -30,8 +32,10 @@ func Setup(datastore *data.Datastore) {
}
func initializeBrowserPushIfNeeded() {
pubKey, _ := data.GetBrowserPushPublicKey()
privKey, _ := data.GetBrowserPushPrivateKey()
configRepository := configrepository.Get()
pubKey, _ := configRepository.GetBrowserPushPublicKey()
privKey, _ := configRepository.GetBrowserPushPrivateKey()
// We need browser push keys so people can register for pushes.
if pubKey == "" || privKey == "" {
@@ -40,26 +44,27 @@ func initializeBrowserPushIfNeeded() {
log.Errorln("unable to initialize browser push notification keys", err)
}
if err := data.SetBrowserPushPrivateKey(browserPrivateKey); err != nil {
if err := configRepository.SetBrowserPushPrivateKey(browserPrivateKey); err != nil {
log.Errorln("unable to set browser push private key", err)
}
if err := data.SetBrowserPushPublicKey(browserPublicKey); err != nil {
if err := configRepository.SetBrowserPushPublicKey(browserPublicKey); err != nil {
log.Errorln("unable to set browser push public key", err)
}
}
// Enable browser push notifications by default.
if !data.GetHasPerformedInitialNotificationsConfig() {
_ = data.SetBrowserPushConfig(models.BrowserNotificationConfiguration{Enabled: true, GoLiveMessage: config.GetDefaults().FederationGoLiveMessage})
_ = data.SetHasPerformedInitialNotificationsConfig(true)
if !configRepository.GetHasPerformedInitialNotificationsConfig() {
_ = configRepository.SetBrowserPushConfig(models.BrowserNotificationConfiguration{Enabled: true, GoLiveMessage: config.GetDefaults().FederationGoLiveMessage})
_ = configRepository.SetHasPerformedInitialNotificationsConfig(true)
}
}
// New creates a new instance of the Notifier.
func New(datastore *data.Datastore) (*Notifier, error) {
notifier := Notifier{
datastore: datastore,
datastore: datastore,
configRepository: configrepository.Get(),
}
if err := notifier.setupBrowserPush(); err != nil {
@@ -73,13 +78,13 @@ func New(datastore *data.Datastore) (*Notifier, error) {
}
func (n *Notifier) setupBrowserPush() error {
if data.GetBrowserPushConfig().Enabled {
publicKey, err := data.GetBrowserPushPublicKey()
if n.configRepository.GetBrowserPushConfig().Enabled {
publicKey, err := n.configRepository.GetBrowserPushPublicKey()
if err != nil || publicKey == "" {
return errors.Wrap(err, "browser notifier disabled, failed to get browser push public key")
}
privateKey, err := data.GetBrowserPushPrivateKey()
privateKey, err := n.configRepository.GetBrowserPushPrivateKey()
if err != nil || privateKey == "" {
return errors.Wrap(err, "browser notifier disabled, failed to get browser push private key")
}
@@ -99,7 +104,7 @@ func (n *Notifier) notifyBrowserPush() {
log.Errorln("error getting browser push notification destinations", err)
}
for _, destination := range destinations {
unsubscribed, err := n.browser.Send(destination, data.GetServerName(), data.GetBrowserPushConfig().GoLiveMessage)
unsubscribed, err := n.browser.Send(destination, n.configRepository.GetServerName(), n.configRepository.GetBrowserPushConfig().GoLiveMessage)
if unsubscribed {
// If the error is "unsubscribed", then remove the destination from the database.
if err := RemoveNotificationForChannel(BrowserPushNotification, destination); err != nil {
@@ -112,14 +117,14 @@ func (n *Notifier) notifyBrowserPush() {
}
func (n *Notifier) setupDiscord() error {
discordConfig := data.GetDiscordConfig()
discordConfig := n.configRepository.GetDiscordConfig()
if discordConfig.Enabled && discordConfig.Webhook != "" {
var image string
if serverURL := data.GetServerURL(); serverURL != "" {
if serverURL := n.configRepository.GetServerURL(); serverURL != "" {
image = serverURL + "/logo"
}
discordNotifier, err := discord.New(
data.GetServerName(),
n.configRepository.GetServerName(),
image,
discordConfig.Webhook,
)
@@ -132,12 +137,12 @@ func (n *Notifier) setupDiscord() error {
}
func (n *Notifier) notifyDiscord() {
goLiveMessage := data.GetDiscordConfig().GoLiveMessage
streamTitle := data.GetStreamTitle()
goLiveMessage := n.configRepository.GetDiscordConfig().GoLiveMessage
streamTitle := n.configRepository.GetStreamTitle()
if streamTitle != "" {
goLiveMessage += "\n" + streamTitle
}
message := fmt.Sprintf("%s\n\n%s", goLiveMessage, data.GetServerURL())
message := fmt.Sprintf("%s\n\n%s", goLiveMessage, n.configRepository.GetServerURL())
if err := n.discord.Send(message); err != nil {
log.Errorln("error sending discord message", err)
@@ -158,6 +163,7 @@ func (n *Notifier) Notify() {
// RemoveNotificationForChannel removes a notification destination.
func RemoveNotificationForChannel(channel, destination string) error {
log.Debugln("Removing notification for channel", channel)
return data.GetDatastore().GetQueries().RemoveNotificationDestinationForChannel(context.Background(), db.RemoveNotificationDestinationForChannelParams{
Channel: channel,
Destination: destination,

View File

@@ -1,7 +1,7 @@
openapi: 3.1.0
info:
version: 0.2.0
version: 0.2.2
title: Owncast APIs
description: |-
Internal
@@ -2948,7 +2948,7 @@ paths:
content:
application/json:
schema:
$ref: '#/components/schemas/SystemMessage'
$ref: '#/components/schemas/MessageEvent'
responses:
'200':
description: Message sent successfully
@@ -3045,7 +3045,7 @@ paths:
content:
application/json:
schema:
$ref: '#/components/schemas/UserMessage'
$ref: '#/components/schemas/MessageEvent'
responses:
'200':
description: Message sent successfully
@@ -3079,7 +3079,7 @@ paths:
content:
application/json:
schema:
$ref: '#/components/schemas/SystemActionEvent'
$ref: '#/components/schemas/MessageEvent'
responses:
'200':
description: Action sent successfully
@@ -3134,6 +3134,26 @@ paths:
responses:
'204':
$ref: '#/components/responses/204'
/integrations/status:
get:
summary: Get the server's status
operationId: ExternalGetStatus
tags: ['External']
security:
- BearerAuth: []
responses:
'200':
description: Status
content:
application/json:
schema:
$ref: '#/components/schemas/Status'
'401':
$ref: '#/components/responses/401BasicAuth'
'404':
$ref: '#/components/responses/404'
default:
$ref: '#/components/responses/Default'
/integrations/streamtitle:
post:
summary: Stream title
@@ -3217,11 +3237,39 @@ paths:
responses:
'204':
$ref: '#/components/responses/204'
/integrations/moderation/chat/user/{userId}:
get:
summary: Get a user's details
operationId: ExternalGetUserDetails
tags: ['External', 'Chat']
security:
- BearerAuth: []
parameters:
- in: path
name: userId
schema:
type: string
description: The ID of the user to find
required: true
responses:
'200':
description: User information
content:
application/json:
schema:
$ref: '#/components/schemas/ModerationUserDetails'
'401':
$ref: '#/components/responses/401BasicAuth'
'404':
$ref: '#/components/responses/404'
default:
$ref: '#/components/responses/Default'
/moderation/chat/user/{userId}:
get:
summary: Get a user's details
operationId: GetUserDetails
tags: ['External', 'Chat']
tags: ['Chat']
parameters:
- in: path
name: userId
@@ -3282,7 +3330,6 @@ paths:
tags: ['Internal', 'Auth', 'Chat']
parameters:
- $ref: '#/components/parameters/IndieAuthState'
- $ref: '#/components/parameters/IndieAuthCode'
responses:
'307':
description: Redirected to home page
@@ -3300,7 +3347,6 @@ paths:
- $ref: '#/components/parameters/IndieAuthRedirectURI'
- $ref: '#/components/parameters/IndieAuthCodeChallenge'
- $ref: '#/components/parameters/IndieAuthState'
- $ref: '#/components/parameters/IndieAuthCode'
responses:
'200':
description: IndieAuth flow concluded
@@ -4402,12 +4448,6 @@ components:
schema:
type: string
required: true
IndieAuthCode:
in: query
name: code
schema:
type: string
required: true
IndieAuthMe:
in: query
name: me

6
package-lock.json generated Normal file
View File

@@ -0,0 +1,6 @@
{
"name": "owncast",
"lockfileVersion": 3,
"requires": true,
"packages": {}
}

Some files were not shown because too many files have changed in this diff Show More