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' if: github.event_name == 'pull_request'
with: with:
repository: ${{ github.event.pull_request.head.repo.full_name }} repository: ${{ github.event.pull_request.head.repo.full_name }}
ref: ${{ github.event.pull_request.head.ref }}
- name: Check out repository code - name: Check out repository code
uses: actions/checkout@v4 uses: actions/checkout@v4

View File

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

View File

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

View File

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

View File

@@ -3,7 +3,12 @@ on:
push: push:
branches: branches:
- develop - 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: jobs:
build-and-deploy: build-and-deploy:
@@ -16,6 +21,7 @@ jobs:
if: github.event_name == 'pull_request' if: github.event_name == 'pull_request'
with: with:
repository: ${{ github.event.pull_request.head.repo.full_name }} repository: ${{ github.event.pull_request.head.repo.full_name }}
ref: ${{ github.event.pull_request.head.ref }}
- name: Check out repository code - name: Check out repository code
uses: actions/checkout@v4 uses: actions/checkout@v4
@@ -43,6 +49,7 @@ jobs:
npm run build-storybook -- -o ../docs/components npm run build-storybook -- -o ../docs/components
- name: Commit changes - name: Commit changes
if: github.repository == 'owncast/owncast'
uses: EndBug/add-and-commit@v9 uses: EndBug/add-and-commit@v9
with: with:
author_name: Owncast author_name: Owncast
@@ -54,6 +61,7 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Dispatch event to web site - name: Dispatch event to web site
if: github.repository == 'owncast/owncast'
uses: peter-evans/repository-dispatch@v3 uses: peter-evans/repository-dispatch@v3
with: with:
token: ${{ secrets.BUNDLE_STORYBOOK_OWNCAST_ONLINE }} token: ${{ secrets.BUNDLE_STORYBOOK_OWNCAST_ONLINE }}

View File

@@ -7,7 +7,7 @@ on:
push: push:
paths: paths:
- web/** - web/**
pull_request_target: pull_request:
paths: paths:
- web/** - web/**
@@ -16,7 +16,6 @@ jobs:
chromatic-deployment: chromatic-deployment:
# Operating System # Operating System
runs-on: ubuntu-latest runs-on: ubuntu-latest
if: github.repository == 'owncast/owncast'
defaults: defaults:
run: run:
@@ -28,15 +27,16 @@ jobs:
with: with:
concurrent_skipping: 'same_content_newer' concurrent_skipping: 'same_content_newer'
- name: Check out pull request code - name: Check out repository code (Push)
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
if: github.event_name == '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 - name: Get changed files
id: changed-files-yaml id: changed-files-yaml

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -121,6 +121,7 @@ jobs:
if: github.event_name == 'pull_request' if: github.event_name == 'pull_request'
with: with:
repository: ${{ github.event.pull_request.head.repo.full_name }} repository: ${{ github.event.pull_request.head.repo.full_name }}
ref: ${{ github.event.pull_request.head.ref }}
- name: Check out repository code - name: Check out repository code
uses: actions/checkout@v4 uses: actions/checkout@v4
@@ -191,6 +192,7 @@ jobs:
if: github.event_name == 'pull_request' if: github.event_name == 'pull_request'
with: with:
repository: ${{ github.event.pull_request.head.repo.full_name }} repository: ${{ github.event.pull_request.head.repo.full_name }}
ref: ${{ github.event.pull_request.head.ref }}
- name: Check out repository code - name: Check out repository code
uses: actions/checkout@v4 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-stale: 60
days-before-issue-close: 67 days-before-issue-close: 67
exempt-issue-labels: backlog,long-lived,bot exempt-issue-labels: backlog,long-lived,bot
exempt-all-issue-milestones: true
stale-issue-message: > stale-issue-message: >
This issue has been automatically marked as stale because it has not had 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 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-stale: 30
days-before-pr-close: 37 days-before-pr-close: 37
exempt-pr-labels: backlog,long-lived,bot
exempt-all-pr-milestones: true
stale-pr-message: > stale-pr-message: >
This pull request has not had any activity in 30 days. If it has been abandoned 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 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 *.dll
*.so *.so
*.dylib *.dylib
.DS_Store
# Test binary, built with `go test -c` # Test binary, built with `go test -c`
*.test *.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 . 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 # 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 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 RUN addgroup -g 101 -S owncast && adduser -u 101 -S owncast -G owncast

View File

@@ -1,6 +1,6 @@
VERSION --new-platform 0.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 ARG version=develop
WORKDIR /build WORKDIR /build
@@ -119,7 +119,7 @@ docker:
# in as space separated strings using the full account/repo:tag format. # 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 # https://github.com/earthly/earthly/blob/aea38448fa9c0064b1b70d61be717ae740689fb9/docs/earthfile/earthfile.md#assigning-multiple-image-names
ARG TARGETPLATFORM 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 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 RUN addgroup -g 101 -S owncast && adduser -u 101 -S owncast -G owncast
WORKDIR /app WORKDIR /app

View File

@@ -1,11 +1,14 @@
package activitypub package activitypub
import ( import (
"math"
"github.com/owncast/owncast/activitypub/crypto" "github.com/owncast/owncast/activitypub/crypto"
"github.com/owncast/owncast/activitypub/inbox" "github.com/owncast/owncast/activitypub/inbox"
"github.com/owncast/owncast/activitypub/outbox" "github.com/owncast/owncast/activitypub/outbox"
"github.com/owncast/owncast/activitypub/persistence" "github.com/owncast/owncast/activitypub/persistence"
"github.com/owncast/owncast/activitypub/workerpool" "github.com/owncast/owncast/activitypub/workerpool"
"github.com/owncast/owncast/persistence/configrepository"
"github.com/owncast/owncast/core/data" "github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/models" "github.com/owncast/owncast/models"
@@ -14,21 +17,35 @@ import (
// Start will initialize and start the federation support. // Start will initialize and start the federation support.
func Start(datastore *data.Datastore) { func Start(datastore *data.Datastore) {
configRepository := configrepository.Get()
persistence.Setup(datastore) persistence.Setup(datastore)
workerpool.InitOutboundWorkerPool()
outboundWorkerPoolSize := getOutboundWorkerPoolSize()
workerpool.InitOutboundWorkerPool(outboundWorkerPoolSize)
inbox.InitInboxWorkerPool() inbox.InitInboxWorkerPool()
// Generate the keys for signing federated activity if needed. // Generate the keys for signing federated activity if needed.
if data.GetPrivateKey() == "" { if configRepository.GetPrivateKey() == "" {
privateKey, publicKey, err := crypto.GenerateKeys() privateKey, publicKey, err := crypto.GenerateKeys()
_ = data.SetPrivateKey(string(privateKey)) _ = configRepository.SetPrivateKey(string(privateKey))
_ = data.SetPublicKey(string(publicKey)) _ = configRepository.SetPublicKey(string(publicKey))
if err != nil { if err != nil {
log.Errorln("Unable to get private key", err) 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. // SendLive will send a "Go Live" message to followers.
func SendLive() error { func SendLive() error {
return outbox.SendLive() return outbox.SendLive()

View File

@@ -6,7 +6,7 @@ import (
"github.com/go-fed/activity/streams" "github.com/go-fed/activity/streams"
"github.com/go-fed/activity/streams/vocab" "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. // 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 // MakeActivityPublic sets the required properties to make this activity
// seen as public. // seen as public.
func MakeActivityPublic(activity vocab.ActivityStreamsCreate) vocab.ActivityStreamsCreate { func MakeActivityPublic(activity vocab.ActivityStreamsCreate) vocab.ActivityStreamsCreate {
configRepository := configrepository.Get()
// TO the public if we're not treating ActivityPub as "private". // TO the public if we're not treating ActivityPub as "private".
if !data.GetFederationIsPrivate() { if !configRepository.GetFederationIsPrivate() {
public, _ := url.Parse(PUBLIC) public, _ := url.Parse(PUBLIC)
to := streams.NewActivityStreamsToProperty() to := streams.NewActivityStreamsToProperty()
@@ -121,7 +123,9 @@ func MakeUpdateActivity(activityID *url.URL) vocab.ActivityStreamsUpdate {
activity.SetJSONLDId(id) activity.SetJSONLDId(id)
// CC the public if we're not treating ActivityPub as "private". // CC the public if we're not treating ActivityPub as "private".
if !data.GetFederationIsPrivate() { configRepository := configrepository.Get()
if !configRepository.GetFederationIsPrivate() {
public, _ := url.Parse(PUBLIC) public, _ := url.Parse(PUBLIC)
cc := streams.NewActivityStreamsCcProperty() cc := streams.NewActivityStreamsCcProperty()
cc.AppendIRI(public) cc.AppendIRI(public)

View File

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

View File

@@ -8,7 +8,7 @@ import (
"github.com/go-fed/activity/streams" "github.com/go-fed/activity/streams"
"github.com/go-fed/activity/streams/vocab" "github.com/go-fed/activity/streams/vocab"
"github.com/owncast/owncast/core/data" "github.com/owncast/owncast/persistence/configrepository"
log "github.com/sirupsen/logrus" 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. // MakeLocalIRIForResource will create an IRI for the local server.
func MakeLocalIRIForResource(resourcePath string) *url.URL { func MakeLocalIRIForResource(resourcePath string) *url.URL {
host := data.GetServerURL() configRepository := configrepository.Get()
host := configRepository.GetServerURL()
u, err := url.Parse(host) u, err := url.Parse(host)
if err != nil { if err != nil {
log.Errorln("unable to parse local IRI url", host, err) 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. // MakeLocalIRIForAccount will return a full IRI for the local server account username.
func MakeLocalIRIForAccount(account string) *url.URL { func MakeLocalIRIForAccount(account string) *url.URL {
host := data.GetServerURL() configRepository := configrepository.Get()
host := configRepository.GetServerURL()
u, err := url.Parse(host) u, err := url.Parse(host)
if err != nil { if err != nil {
log.Errorln("unable to parse local IRI account server url", err) 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. // MakeLocalIRIForStreamURL will return a full IRI for the local server stream url.
func MakeLocalIRIForStreamURL() *url.URL { func MakeLocalIRIForStreamURL() *url.URL {
host := data.GetServerURL() configRepository := configrepository.Get()
host := configRepository.GetServerURL()
u, err := url.Parse(host) u, err := url.Parse(host)
if err != nil { if err != nil {
log.Errorln("unable to parse local IRI stream url", err) 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. // MakeLocalIRIforLogo will return a full IRI for the local server logo.
func MakeLocalIRIforLogo() *url.URL { func MakeLocalIRIforLogo() *url.URL {
host := data.GetServerURL() configRepository := configrepository.Get()
host := configRepository.GetServerURL()
u, err := url.Parse(host) u, err := url.Parse(host)
if err != nil { if err != nil {
log.Errorln("unable to parse local IRI stream url", err) 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 // GetLogoType will return the rel value for the webfinger response and
// the default static image is of type png. // the default static image is of type png.
func GetLogoType() string { func GetLogoType() string {
imageFilename := data.GetLogoPath() configRepository := configrepository.Get()
imageFilename := configRepository.GetLogoPath()
if imageFilename == "" { if imageFilename == "" {
return "image/png" return "image/png"
} }

View File

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

View File

@@ -16,7 +16,7 @@ import (
"github.com/owncast/owncast/activitypub/crypto" "github.com/owncast/owncast/activitypub/crypto"
"github.com/owncast/owncast/activitypub/persistence" "github.com/owncast/owncast/activitypub/persistence"
"github.com/owncast/owncast/activitypub/requests" "github.com/owncast/owncast/activitypub/requests"
"github.com/owncast/owncast/core/data" "github.com/owncast/owncast/persistence/configrepository"
) )
const ( const (
@@ -145,7 +145,9 @@ func getFollowersPage(page string, r *http.Request) (vocab.ActivityStreamsOrdere
} }
func createPageURL(r *http.Request, page *string) (*url.URL, error) { func createPageURL(r *http.Request, page *string) (*url.URL, error) {
domain := data.GetServerURL() configRepository := configrepository.Get()
domain := configRepository.GetServerURL()
if domain == "" { if domain == "" {
return nil, errors.New("unable to get server URL") 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/apmodels"
"github.com/owncast/owncast/activitypub/inbox" "github.com/owncast/owncast/activitypub/inbox"
"github.com/owncast/owncast/core/data" "github.com/owncast/owncast/persistence/configrepository"
log "github.com/sirupsen/logrus" 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) { func acceptInboxRequest(w http.ResponseWriter, r *http.Request) {
if !data.GetFederationEnabled() { configRepository := configrepository.Get()
if !configRepository.GetFederationEnabled() {
w.WriteHeader(http.StatusMethodNotAllowed) w.WriteHeader(http.StatusMethodNotAllowed)
return 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 // The account this request is for must match the account name we have set
// for federation. // for federation.
if forLocalAccount != data.GetFederationUsername() { if forLocalAccount != configRepository.GetFederationUsername() {
w.WriteHeader(http.StatusNotFound) w.WriteHeader(http.StatusNotFound)
return return
} }

View File

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

View File

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

View File

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

View File

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

View File

@@ -7,17 +7,19 @@ import (
"github.com/owncast/owncast/activitypub/resolvers" "github.com/owncast/owncast/activitypub/resolvers"
"github.com/owncast/owncast/core/chat" "github.com/owncast/owncast/core/chat"
"github.com/owncast/owncast/core/chat/events" "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 { 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. // Do nothing if displaying engagement actions has been turned off.
if !data.GetFederationShowEngagement() { if !configRepository.GetFederationShowEngagement() {
return nil return nil
} }
// Do nothing if chat is disabled // Do nothing if chat is disabled
if data.GetChatDisabled() { if configRepository.GetChatDisabled() {
return nil return nil
} }
@@ -36,11 +38,11 @@ func handleEngagementActivity(eventType events.EventType, isLiveNotification boo
if isLiveNotification && action == events.FediverseEngagementLike { if isLiveNotification && action == events.FediverseEngagementLike {
suffix = "liked that this stream went live." suffix = "liked that this stream went live."
} else if action == events.FediverseEngagementLike { } 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 { } else if isLiveNotification && action == events.FediverseEngagementRepost {
suffix = "shared this stream with their followers." suffix = "shared this stream with their followers."
} else if action == events.FediverseEngagementRepost { } 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 { } else if action == events.FediverseEngagementFollow {
suffix = "followed this stream." suffix = "followed this stream."
} else { } else {

View File

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

View File

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

View File

@@ -9,6 +9,7 @@ import (
"github.com/owncast/owncast/activitypub/apmodels" "github.com/owncast/owncast/activitypub/apmodels"
"github.com/owncast/owncast/activitypub/persistence" "github.com/owncast/owncast/activitypub/persistence"
"github.com/owncast/owncast/core/data" "github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/persistence/configrepository"
) )
func makeFakePerson() vocab.ActivityStreamsPerson { func makeFakePerson() vocab.ActivityStreamsPerson {
@@ -49,21 +50,24 @@ func makeFakePerson() vocab.ActivityStreamsPerson {
func TestMain(m *testing.M) { func TestMain(m *testing.M) {
data.SetupPersistence(":memory:") data.SetupPersistence(":memory:")
data.SetServerURL("https://my.cool.site.biz") configRepository := configrepository.Get()
configRepository.SetServerURL("https://my.cool.site.biz")
persistence.Setup(data.GetDatastore()) persistence.Setup(data.GetDatastore())
m.Run() m.Run()
} }
func TestBlockedDomains(t *testing.T) { func TestBlockedDomains(t *testing.T) {
configRepository := configrepository.Get()
person := makeFakePerson() 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") 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 { if domain == person.GetJSONLDId().GetIRI().Host {
return return
} }

View File

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

View File

@@ -3,6 +3,7 @@ package persistence
import ( import (
"context" "context"
"database/sql" "database/sql"
"encoding/json"
"fmt" "fmt"
"net/url" "net/url"
"time" "time"
@@ -74,6 +75,27 @@ func GetFollower(iri string) (*apmodels.ActivityPubActor, error) {
return nil, errors.Wrap(err, "error parsing acting inbox") 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) image, _ := url.Parse(result.Image.String)
var disabledAt *time.Time var disabledAt *time.Time
@@ -89,6 +111,7 @@ func GetFollower(iri string) (*apmodels.ActivityPubActor, error) {
Image: image, Image: image,
FollowRequestIri: followIRI, FollowRequestIri: followIRI,
DisabledAt: disabledAt, DisabledAt: disabledAt,
RequestObject: followRequestObject,
} }
return &follower, nil return &follower, nil

View File

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

View File

@@ -2,14 +2,10 @@ package workerpool
import ( import (
"net/http" "net/http"
"runtime"
log "github.com/sirupsen/logrus" 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. // Job struct bundling the ActivityPub and the payload in one struct.
type Job struct { type Job struct {
request *http.Request request *http.Request
@@ -18,8 +14,8 @@ type Job struct {
var queue chan Job var queue chan Job
// InitOutboundWorkerPool starts n go routines that await ActivityPub jobs. // InitOutboundWorkerPool starts n go routines that await ActivityPub jobs.
func InitOutboundWorkerPool() { func InitOutboundWorkerPool(workerPoolSize int) {
queue = make(chan Job) queue = make(chan Job, workerPoolSize)
// start workers // start workers
for i := 1; i <= workerPoolSize; i++ { for i := 1; i <= workerPoolSize; i++ {
@@ -29,8 +25,13 @@ func InitOutboundWorkerPool() {
// AddToOutboundQueue will queue up an outbound http request. // AddToOutboundQueue will queue up an outbound http request.
func AddToOutboundQueue(req *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) log.Tracef("Queued request for ActivityPub destination %s", req.RequestURI)
queue <- Job{req}
} }
func worker(workerID int, queue <-chan Job) { func worker(workerID int, queue <-chan Job) {

View File

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

View File

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

View File

@@ -4,7 +4,7 @@ import "path/filepath"
const ( const (
// StaticVersionNumber is the version of Owncast that is used when it's not overwritten via build-time settings. // 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 is the version of ffmpeg we suggest.
FfmpegSuggestedVersion = "v4.1.5" // Requires the v FfmpegSuggestedVersion = "v4.1.5" // Requires the v
// DataDirectory is the directory we save data to. // DataDirectory is the directory we save data to.

View File

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

View File

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

View File

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

View File

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

View File

@@ -162,9 +162,9 @@ func loadEmoji() {
if err != nil { if err != nil {
return 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) emojiArr = append(emojiArr, emoji)
} }

View File

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

View File

@@ -1,6 +1,8 @@
package events 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. // SystemMessageEvent is a message displayed in chat on behalf of the server.
type SystemMessageEvent struct { type SystemMessageEvent struct {
@@ -10,13 +12,15 @@ type SystemMessageEvent struct {
// GetBroadcastPayload will return the object to send to all chat users. // GetBroadcastPayload will return the object to send to all chat users.
func (e *SystemMessageEvent) GetBroadcastPayload() EventPayload { func (e *SystemMessageEvent) GetBroadcastPayload() EventPayload {
configRepository := configrepository.Get()
return EventPayload{ return EventPayload{
"id": e.ID, "id": e.ID,
"timestamp": e.Timestamp, "timestamp": e.Timestamp,
"body": e.Body, "body": e.Body,
"type": SystemMessageSent, "type": SystemMessageSent,
"user": EventPayload{ "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/chat/events"
"github.com/owncast/owncast/core/webhooks" "github.com/owncast/owncast/core/webhooks"
"github.com/owncast/owncast/persistence/chatmessagerepository"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
) )
// SetMessagesVisibility will set the visibility of multiple messages by ID. // SetMessagesVisibility will set the visibility of multiple messages by ID.
func SetMessagesVisibility(messageIDs []string, visibility bool) error { func SetMessagesVisibility(messageIDs []string, visibility bool) error {
// Save new message visibility // 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) log.Errorln(err)
return err return err
} }

View File

@@ -1,30 +1,25 @@
package chat package chat
import ( import (
"context"
"database/sql"
"strings"
"time" "time"
"github.com/owncast/owncast/core/chat/events"
"github.com/owncast/owncast/core/data" "github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/models" "github.com/owncast/owncast/persistence/authrepository"
"github.com/owncast/owncast/persistence/tables" "github.com/owncast/owncast/persistence/tables"
log "github.com/sirupsen/logrus"
) )
var _datastore *data.Datastore var _datastore *data.Datastore
const ( const (
maxBacklogHours = 2 // Keep backlog max hours worth of messages maxBacklogHours = 2 // Keep backlog max hours worth of messages
maxBacklogNumber = 50 // Return max number of messages in history request
) )
func setupPersistence() { func setupPersistence() {
_datastore = data.GetDatastore() _datastore = data.GetDatastore()
tables.CreateMessagesTable(_datastore.DB) tables.CreateMessagesTable(_datastore.DB)
data.CreateBanIPTable(_datastore.DB)
authRepository := authrepository.Get()
authRepository.CreateBanIPTable(_datastore.DB)
chatDataPruner := time.NewTicker(5 * time.Minute) chatDataPruner := time.NewTicker(5 * time.Minute)
go func() { 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/config"
"github.com/owncast/owncast/core/chat/events" "github.com/owncast/owncast/core/chat/events"
"github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/core/webhooks" "github.com/owncast/owncast/core/webhooks"
"github.com/owncast/owncast/models" "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/persistence/userrepository"
"github.com/owncast/owncast/services/geoip" "github.com/owncast/owncast/services/geoip"
"github.com/owncast/owncast/utils" "github.com/owncast/owncast/utils"
@@ -95,7 +96,9 @@ func (s *Server) Addclient(conn *websocket.Conn, user *models.User, accessToken
ConnectedAt: time.Now(), ConnectedAt: time.Now(),
} }
shouldSendJoinedMessages := data.GetChatJoinPartMessagesEnabled() configRepository := configrepository.Get()
shouldSendJoinedMessages := configRepository.GetChatJoinPartMessagesEnabled()
// If there are existing clients connected for this user do not send // If there are existing clients connected for this user do not send
// a user joined message. Do not put this under a mutex, as // 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.User = c.User
userPartEvent.ClientID = c.Id userPartEvent.ClientID = c.Id
configRepository := configrepository.Get()
// If part messages are disabled. // If part messages are disabled.
if data.GetChatJoinPartMessagesEnabled() { if configRepository.GetChatJoinPartMessagesEnabled() {
if err := s.Broadcast(userPartEvent.GetBroadcastPayload()); err != nil { if err := s.Broadcast(userPartEvent.GetBroadcastPayload()); err != nil {
log.Errorln("error sending chat part message", err) 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. // HandleClientConnection is fired when a single client connects to the websocket.
func (s *Server) HandleClientConnection(w http.ResponseWriter, r *http.Request) { 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)) _, _ = w.Write([]byte(events.ChatDisabled))
return return
} }
ipAddress := utils.GetIPAddressFromRequest(r) ipAddress := utils.GetIPAddressFromRequest(r)
// Check if this client's IP address is banned. If so send a rejection. // 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.") log.Debugln("Client ip address has been blocked. Rejecting.")
w.WriteHeader(http.StatusForbidden) w.WriteHeader(http.StatusForbidden)
@@ -377,12 +385,14 @@ func SendActionToUser(userID string, text string) error {
} }
func (s *Server) eventReceived(event chatClientEvent) { func (s *Server) eventReceived(event chatClientEvent) {
configRepository := configrepository.Get()
c := event.client c := event.client
u := c.User u := c.User
// If established chat user only mode is enabled and the user is not old // 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. // 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.") 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 return
} }
@@ -409,10 +419,12 @@ func (s *Server) eventReceived(event chatClientEvent) {
} }
func (s *Server) sendWelcomeMessageToClient(c *Client) { func (s *Server) sendWelcomeMessageToClient(c *Client) {
configRepository := configrepository.Get()
// Add an artificial delay so people notice this message come in. // Add an artificial delay so people notice this message come in.
time.Sleep(7 * time.Second) time.Sleep(7 * time.Second)
welcomeMessage := utils.RenderSimpleMarkdown(data.GetServerWelcomeMessage()) welcomeMessage := utils.RenderSimpleMarkdown(configRepository.GetServerWelcomeMessage())
if welcomeMessage != "" { if welcomeMessage != "" {
s.sendSystemMessageToClient(c, welcomeMessage) s.sendSystemMessageToClient(c, welcomeMessage)
@@ -420,7 +432,9 @@ func (s *Server) sendWelcomeMessageToClient(c *Client) {
} }
func (s *Server) sendAllWelcomeMessage() { func (s *Server) sendAllWelcomeMessage() {
welcomeMessage := utils.RenderSimpleMarkdown(data.GetServerWelcomeMessage()) configRepository := configrepository.Get()
welcomeMessage := utils.RenderSimpleMarkdown(configRepository.GetServerWelcomeMessage())
if welcomeMessage != "" { if welcomeMessage != "" {
clientMessage := events.SystemMessageEvent{ clientMessage := events.SystemMessageEvent{

View File

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

View File

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

View File

@@ -5,12 +5,11 @@ import (
"database/sql" "database/sql"
"encoding/gob" "encoding/gob"
"sync" "sync"
"time"
// sqlite requires a blank import. // sqlite requires a blank import.
_ "github.com/mattn/go-sqlite3" _ "github.com/mattn/go-sqlite3"
"github.com/owncast/owncast/config"
"github.com/owncast/owncast/db" "github.com/owncast/owncast/db"
"github.com/owncast/owncast/models"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
) )
@@ -21,7 +20,8 @@ type Datastore struct {
DbLock *sync.Mutex 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") log.Traceln("Warming config value cache")
res, err := ds.DB.Query("SELECT key, value FROM datastore") 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. // 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) cachedValue, err := ds.GetCachedValue(key)
if err == nil { if err == nil {
return ConfigEntry{ return models.ConfigEntry{
Key: key, Key: key,
Value: cachedValue, Value: cachedValue,
}, nil }, 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) row := ds.DB.QueryRow("SELECT key, value FROM datastore WHERE key = ? LIMIT 1", key)
if err := row.Scan(&resultKey, &resultValue); err != nil { if err := row.Scan(&resultKey, &resultValue); err != nil {
return ConfigEntry{}, err return models.ConfigEntry{}, err
} }
result := ConfigEntry{ result := models.ConfigEntry{
Key: resultKey, Key: resultKey,
Value: resultValue, Value: resultValue,
} }
@@ -73,7 +73,7 @@ func (ds *Datastore) Get(key string) (ConfigEntry, error) {
} }
// Save will save the ConfigEntry to the database. // 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() ds.DbLock.Lock()
defer ds.DbLock.Unlock() defer ds.DbLock.Unlock()
@@ -93,7 +93,6 @@ func (ds *Datastore) Save(e ConfigEntry) error {
return err return err
} }
_, err = stmt.Exec(e.Key, dataGob.Bytes()) _, err = stmt.Exec(e.Key, dataGob.Bytes())
if err != nil { if err != nil {
return err return err
} }
@@ -108,39 +107,11 @@ func (ds *Datastore) Save(e ConfigEntry) error {
return nil return nil
} }
// Setup will create the datastore table and perform initial initialization. // Setup will perform initial initialization.
func (ds *Datastore) Setup() { func (ds *Datastore) Setup() {
ds.cache = make(map[string][]byte) ds.cache = make(map[string][]byte)
ds.DB = GetDatabase() ds.DB = GetDatabase()
ds.DbLock = &sync.Mutex{} 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. // 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 { if _, err = stmt.Exec(); err != nil {
log.Fatalln(err) log.Fatalln(err)
} }
PopulateDefaults()
} }
// GetDatastore returns the shared instance of the owncast datastore. // 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" "time"
"github.com/owncast/owncast/config" "github.com/owncast/owncast/config"
"github.com/owncast/owncast/models"
"github.com/owncast/owncast/static" "github.com/owncast/owncast/static"
"github.com/owncast/owncast/utils" "github.com/owncast/owncast/utils"
"github.com/owncast/owncast/webserver/handlers/generated"
"github.com/pkg/errors" "github.com/pkg/errors"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
) )
var ( var (
emojiCacheMu sync.Mutex emojiCacheMu sync.Mutex
emojiCacheData = make([]models.CustomEmoji, 0) emojiCacheData = make([]generated.Emoji, 0)
emojiCacheModTime time.Time emojiCacheModTime time.Time
) )
@@ -51,7 +51,7 @@ func UpdateEmojiList(force bool) (time.Time, error) {
return modTime, fmt.Errorf("unable to open custom emoji directory") 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 { walkFunction := func(path string, d os.DirEntry, err error) error {
if d == nil || d.IsDir() { if d == nil || d.IsDir() {
@@ -61,7 +61,7 @@ func UpdateEmojiList(force bool) (time.Time, error) {
emojiPath := filepath.Join(config.EmojiDir, path) emojiPath := filepath.Join(config.EmojiDir, path)
fileName := d.Name() fileName := d.Name()
fileBase := fileName[:len(fileName)-len(filepath.Ext(fileName))] 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) emojiCacheData = append(emojiCacheData, singleEmoji)
return nil return nil
} }
@@ -76,7 +76,7 @@ func UpdateEmojiList(force bool) (time.Time, error) {
} }
// GetEmojiList returns a list of custom emoji from the emoji directory. // GetEmojiList returns a list of custom emoji from the emoji directory.
func GetEmojiList() []models.CustomEmoji { func GetEmojiList() []generated.Emoji {
_, err := UpdateEmojiList(false) _, err := UpdateEmojiList(false)
if err != nil { if err != nil {
return nil return nil
@@ -88,7 +88,7 @@ func GetEmojiList() []models.CustomEmoji {
// return a copy of cache data, ensures underlying slice isn't affected // return a copy of cache data, ensures underlying slice isn't affected
// by future update // by future update
emojiData := make([]models.CustomEmoji, len(emojiCacheData)) emojiData := make([]generated.Emoji, len(emojiCacheData))
copy(emojiData, emojiCacheData) copy(emojiData, emojiCacheData)
return emojiData 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 package data
import "github.com/owncast/owncast/models"
// GetStringSlice will return the string slice value for a key. // GetStringSlice will return the string slice value for a key.
func (ds *Datastore) GetStringSlice(key string) ([]string, error) { func (ds *Datastore) GetStringSlice(key string) ([]string, error) {
configEntry, err := ds.Get(key) configEntry, err := ds.Get(key)
if err != nil { if err != nil {
return []string{}, err return []string{}, err
} }
return configEntry.getStringSlice() return configEntry.GetStringSlice()
} }
// SetStringSlice will set the string slice value for a key. // SetStringSlice will set the string slice value for a key.
func (ds *Datastore) SetStringSlice(key string, value []string) error { func (ds *Datastore) SetStringSlice(key string, value []string) error {
configEntry := ConfigEntry{value, key} configEntry := models.ConfigEntry{Value: value, Key: key}
return ds.Save(configEntry) return ds.Save(configEntry)
} }
@@ -21,12 +23,12 @@ func (ds *Datastore) GetString(key string) (string, error) {
if err != nil { if err != nil {
return "", err return "", err
} }
return configEntry.getString() return configEntry.GetString()
} }
// SetString will set the string value for a key. // SetString will set the string value for a key.
func (ds *Datastore) SetString(key string, value string) error { func (ds *Datastore) SetString(key string, value string) error {
configEntry := ConfigEntry{value, key} configEntry := models.ConfigEntry{Value: value, Key: key}
return ds.Save(configEntry) return ds.Save(configEntry)
} }
@@ -36,12 +38,12 @@ func (ds *Datastore) GetNumber(key string) (float64, error) {
if err != nil { if err != nil {
return 0, err return 0, err
} }
return configEntry.getNumber() return configEntry.GetNumber()
} }
// SetNumber will set the numeric value for a key. // SetNumber will set the numeric value for a key.
func (ds *Datastore) SetNumber(key string, value float64) error { func (ds *Datastore) SetNumber(key string, value float64) error {
configEntry := ConfigEntry{value, key} configEntry := models.ConfigEntry{Value: value, Key: key}
return ds.Save(configEntry) return ds.Save(configEntry)
} }
@@ -51,12 +53,12 @@ func (ds *Datastore) GetBool(key string) (bool, error) {
if err != nil { if err != nil {
return false, err return false, err
} }
return configEntry.getBool() return configEntry.GetBool()
} }
// SetBool will set the boolean value for a key. // SetBool will set the boolean value for a key.
func (ds *Datastore) SetBool(key string, value bool) error { func (ds *Datastore) SetBool(key string, value bool) error {
configEntry := ConfigEntry{value, key} configEntry := models.ConfigEntry{Value: value, Key: key}
return ds.Save(configEntry) return ds.Save(configEntry)
} }
@@ -66,11 +68,11 @@ func (ds *Datastore) GetStringMap(key string) (map[string]string, error) {
if err != nil { if err != nil {
return map[string]string{}, err return map[string]string{}, err
} }
return configEntry.getStringMap() return configEntry.GetStringMap()
} }
// SetStringMap will set the string map value for a key. // SetStringMap will set the string map value for a key.
func (ds *Datastore) SetStringMap(key string, value map[string]string) error { 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) return ds.Save(configEntry)
} }

View File

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

View File

@@ -7,8 +7,8 @@ import (
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/models" "github.com/owncast/owncast/models"
"github.com/owncast/owncast/persistence/configrepository"
"github.com/owncast/owncast/services/geoip" "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. // 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. // So account for that with an artificial buffer of four segments.
timeSinceLastConnected := time.Since(_stats.LastConnectTime.Time).Seconds() 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 { if timeSinceLastConnected < waitTime {
return false return false
} }
@@ -75,7 +76,7 @@ func SetViewerActive(viewer *models.Viewer) {
l.Lock() l.Lock()
defer l.Unlock() defer l.Unlock()
// Asynchronously, optionally, fetch GeoIP data. // Asynchronously, optionally, fetch GeoIP configRepository.
go func(viewer *models.Viewer) { go func(viewer *models.Viewer) {
viewer.Geo = _geoIPClient.GetGeoFromIP(viewer.IPAddress) viewer.Geo = _geoIPClient.GetGeoFromIP(viewer.IPAddress)
}(viewer) }(viewer)
@@ -111,27 +112,29 @@ func pruneViewerCount() {
} }
func saveStats() { 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) 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) log.Errorln("error saving viewer count", err)
} }
if _stats.LastDisconnectTime != nil && _stats.LastDisconnectTime.Valid { 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) log.Errorln("error saving disconnect time", err)
} }
} }
} }
func getSavedStats() models.Stats { func getSavedStats() models.Stats {
savedLastDisconnectTime, _ := data.GetLastDisconnectTime() configRepository := configrepository.Get()
savedLastDisconnectTime, _ := configRepository.GetLastDisconnectTime()
result := models.Stats{ result := models.Stats{
ChatClients: make(map[string]models.Client), ChatClients: make(map[string]models.Client),
Viewers: make(map[string]*models.Viewer), Viewers: make(map[string]*models.Viewer),
SessionMaxViewerCount: data.GetPeakSessionViewerCount(), SessionMaxViewerCount: configRepository.GetPeakSessionViewerCount(),
OverallMaxViewerCount: data.GetPeakOverallViewerCount(), OverallMaxViewerCount: configRepository.GetPeakOverallViewerCount(),
LastDisconnectTime: savedLastDisconnectTime, LastDisconnectTime: savedLastDisconnectTime,
} }

View File

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

View File

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

View File

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

View File

@@ -11,7 +11,7 @@ import (
"sync" "sync"
"time" "time"
"github.com/owncast/owncast/core/data" "github.com/owncast/owncast/persistence/configrepository"
"github.com/owncast/owncast/utils" "github.com/owncast/owncast/utils"
"github.com/pkg/errors" "github.com/pkg/errors"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
@@ -64,9 +64,9 @@ func NewS3Storage() *S3Storage {
// Setup sets up the s3 storage for saving the video to s3. // Setup sets up the s3 storage for saving the video to s3.
func (s *S3Storage) Setup() error { func (s *S3Storage) Setup() error {
log.Trace("Setting up S3 for external storage of video...") log.Trace("Setting up S3 for external storage of video...")
configRepository := configrepository.Get()
s3Config := data.GetS3Config() s3Config := configRepository.GetS3Config()
customVideoServingEndpoint := data.GetVideoServingEndpoint() customVideoServingEndpoint := configRepository.GetVideoServingEndpoint()
if customVideoServingEndpoint != "" { if customVideoServingEndpoint != "" {
s.host = customVideoServingEndpoint s.host = customVideoServingEndpoint
@@ -106,8 +106,9 @@ func (s *S3Storage) SegmentWritten(localFilePath string) {
averagePerformance := utils.GetAveragePerformance(performanceMonitorKey) averagePerformance := utils.GetAveragePerformance(performanceMonitorKey)
// Warn the user about long-running save operations // Warn the user about long-running save operations
configRepository := configrepository.Get()
if averagePerformance != 0 { 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/") 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. // RemoteCleanup will remove old files from the remote storage provider.
func (s *S3Storage) RemoteCleanup() error { func (s *S3Storage) RemoteCleanup() error {
// Determine how many files we should keep on S3 storage // Determine how many files we should keep on S3 storage
maxNumber := data.GetStreamLatencyLevel().SegmentCount configRepository := configrepository.Get()
maxNumber := configRepository.GetStreamLatencyLevel().SegmentCount
buffer := 20 buffer := 20
keys, err := s.getDeletableVideoSegmentsWithOffset(maxNumber + buffer) keys, err := s.getDeletableVideoSegmentsWithOffset(maxNumber + buffer)

View File

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

View File

@@ -27,6 +27,7 @@ var supportedCodecs = map[string]string{
(&Libx264Codec{}).Name(): "libx264", (&Libx264Codec{}).Name(): "libx264",
(&OmxCodec{}).Name(): "omx", (&OmxCodec{}).Name(): "omx",
(&VaapiCodec{}).Name(): "vaapi", (&VaapiCodec{}).Name(): "vaapi",
(&QuicksyncCodec{}).Name(): "qsv",
(&NvencCodec{}).Name(): "NVIDIA nvenc", (&NvencCodec{}).Name(): "NVIDIA nvenc",
(&VideoToolboxCodec{}).Name(): "videotoolbox", (&VideoToolboxCodec{}).Name(): "videotoolbox",
} }
@@ -191,7 +192,7 @@ func (c *VaapiCodec) GlobalFlags() string {
// PixelFormat is the pixel format required for this codec. // PixelFormat is the pixel format required for this codec.
func (c *VaapiCodec) PixelFormat() string { func (c *VaapiCodec) PixelFormat() string {
return "vaapi_vld" return "vaapi"
} }
// Scaler is the scaler used for resizing the video in the transcoder. // 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. // ExtraFilters are the extra filters required for this codec in the transcoder.
func (c *VaapiCodec) ExtraFilters() string { 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. // 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. // GlobalFlags are the global flags used with this codec in the transcoder.
func (c *QuicksyncCodec) GlobalFlags() string { 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. // PixelFormat is the pixel format required for this codec.
func (c *QuicksyncCodec) PixelFormat() string { func (c *QuicksyncCodec) PixelFormat() string {
return "nv12" return "qsv"
} }
// Scaler is the scaler used for resizing the video in the transcoder. // Scaler is the scaler used for resizing the video in the transcoder.
func (c *QuicksyncCodec) Scaler() string { func (c *QuicksyncCodec) Scaler() string {
return "" return "scale_qsv"
} }
// ExtraArguments are the extra arguments used with this codec in the transcoder. // 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. // ExtraFilters are the extra filters required for this codec in the transcoder.
func (c *QuicksyncCodec) ExtraFilters() string { 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. // 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. // GetPresetForLevel returns the string preset for this codec given an integer level.
func (c *QuicksyncCodec) GetPresetForLevel(l int) string { func (c *QuicksyncCodec) GetPresetForLevel(l int) string {
presetMapping := map[int]string{ presetMapping := map[int]string{
0: "ultrafast", 0: "veryfast",
1: "superfast", 1: "fast",
2: "veryfast", 2: "medium",
3: "faster", 3: "slow",
4: "fast", 4: "veryslow",
} }
preset, ok := presetMapping[l] preset, ok := presetMapping[l]
if !ok { if !ok {
defaultPreset := presetMapping[1] defaultPreset := presetMapping[2]
log.Errorf("Invalid level for quicksync preset %d, defaulting to %s", l, defaultPreset) log.Errorf("Invalid level for quicksync preset %d, defaulting to %s", l, defaultPreset)
return defaultPreset return defaultPreset
} }

View File

@@ -11,7 +11,7 @@ import (
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"github.com/owncast/owncast/config" "github.com/owncast/owncast/config"
"github.com/owncast/owncast/core/data" "github.com/owncast/owncast/persistence/configrepository"
"github.com/owncast/owncast/utils" "github.com/owncast/owncast/utils"
) )
@@ -88,9 +88,9 @@ func fireThumbnailGenerator(segmentPath string, variantIndex int) error {
if len(names) == 0 { if len(names) == 0 {
return nil return nil
} }
configRepository := configrepository.Get()
mostRecentFile := path.Join(framePath, names[0]) mostRecentFile := path.Join(framePath, names[0])
ffmpegPath := utils.ValidatedFfmpegPath(data.GetFfMpegPath()) ffmpegPath := utils.ValidatedFfmpegPath(configRepository.GetFfMpegPath())
outputFileTemp := path.Join(config.TempDir, "tempthumbnail.jpg") outputFileTemp := path.Join(config.TempDir, "tempthumbnail.jpg")
thumbnailCmdFlags := []string{ thumbnailCmdFlags := []string{
@@ -120,7 +120,8 @@ func fireThumbnailGenerator(segmentPath string, variantIndex int) error {
} }
func makeAnimatedGifPreview(sourceFile string, outputFile string) { 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") outputFileTemp := path.Join(config.TempDir, "temppreview.gif")
// Filter is pulled from https://engineering.giphy.com/how-to-make-gifs-with-ffmpeg/ // 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/teris-io/shortid"
"github.com/owncast/owncast/config" "github.com/owncast/owncast/config"
"github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/logging" "github.com/owncast/owncast/logging"
"github.com/owncast/owncast/models" "github.com/owncast/owncast/models"
"github.com/owncast/owncast/persistence/configrepository"
"github.com/owncast/owncast/utils" "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 "-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 "-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 "-method PUT", // HLS results sent back to us will be over PUTs
"-http_persistent", "1", // Ensures persistent HTTP connections
localListenerAddress + "/%v/stream.m3u8", // Send HLS playlists back to us over HTTP 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. // NewTranscoder will return a new Transcoder, populated by the config.
func NewTranscoder() *Transcoder { func NewTranscoder() *Transcoder {
ffmpegPath := utils.ValidatedFfmpegPath(data.GetFfMpegPath()) configRepository := configrepository.Get()
ffmpegPath := utils.ValidatedFfmpegPath(configRepository.GetFfMpegPath())
transcoder := new(Transcoder) transcoder := new(Transcoder)
transcoder.ffmpegPath = ffmpegPath transcoder.ffmpegPath = ffmpegPath
transcoder.internalListenerPort = config.InternalHLSListenerPort transcoder.internalListenerPort = config.InternalHLSListenerPort
transcoder.currentStreamOutputSettings = data.GetStreamOutputVariants() transcoder.currentStreamOutputSettings = configRepository.GetStreamOutputVariants()
transcoder.currentLatencyLevel = data.GetStreamLatencyLevel() transcoder.currentLatencyLevel = configRepository.GetStreamLatencyLevel()
transcoder.codec = getCodec(data.GetVideoCodec()) transcoder.codec = getCodec(configRepository.GetVideoCodec())
transcoder.segmentOutputPath = config.HLSStoragePath transcoder.segmentOutputPath = config.HLSStoragePath
transcoder.playlistOutputPath = config.HLSStoragePath transcoder.playlistOutputPath = config.HLSStoragePath

View File

@@ -42,7 +42,7 @@ func TestFFmpegNvencCommand(t *testing.T) {
cmd := transcoder.getString() cmd := transcoder.getString()
expectedLogPath := filepath.Join("data", "logs", "transcoder.log") 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 { if cmd != expected {
t.Errorf("ffmpeg command does not match expected.\nGot %s\n, want: %s", 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() cmd := transcoder.getString()
expectedLogPath := filepath.Join("data", "logs", "transcoder.log") 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 { if cmd != expected {
t.Errorf("ffmpeg command does not match expected.\nGot %s\n, want: %s", 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() cmd := transcoder.getString()
expectedLogPath := filepath.Join("data", "logs", "transcoder.log") 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 { if cmd != expected {
t.Errorf("ffmpeg command does not match expected.\nGot %s\n, want: %s", 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() cmd := transcoder.getString()
expectedLogPath := filepath.Join("data", "logs", "transcoder.log") 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 { if cmd != expected {
t.Errorf("ffmpeg command does not match expected.\nGot %s\n, want: %s", 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() cmd := transcoder.getString()
expectedLogPath := filepath.Join("data", "logs", "transcoder.log") 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 { if cmd != expected {
t.Errorf("ffmpeg command does not match expected.\nGot %s\n, want: %s", 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" "sync"
"github.com/owncast/owncast/config" "github.com/owncast/owncast/config"
"github.com/owncast/owncast/core/data" "github.com/owncast/owncast/persistence/configrepository"
"github.com/owncast/owncast/utils" "github.com/owncast/owncast/utils"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
) )
@@ -59,6 +59,8 @@ var ignoredErrors = []string{
"Non-monotonous DTS in output", "Non-monotonous DTS in output",
"frames duplicated", "frames duplicated",
"To ignore this", "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) { func handleTranscoderMessage(message string) {
@@ -99,9 +101,9 @@ func handleTranscoderMessage(message string) {
func createVariantDirectories() { func createVariantDirectories() {
// Create private hls data dirs // Create private hls data dirs
utils.CleanupDirectory(config.HLSStoragePath) utils.CleanupDirectory(config.HLSStoragePath)
configRepository := configrepository.Get()
if len(data.GetStreamOutputVariants()) != 0 { if len(configRepository.GetStreamOutputVariants()) != 0 {
for index := range data.GetStreamOutputVariants() { for index := range configRepository.GetStreamOutputVariants() {
if err := os.MkdirAll(path.Join(config.HLSStoragePath, strconv.Itoa(index)), 0o750); err != nil { if err := os.MkdirAll(path.Join(config.HLSStoragePath, strconv.Itoa(index)), 0o750); err != nil {
log.Fatalln(err) log.Fatalln(err)
} }

View File

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

View File

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

View File

@@ -4,8 +4,8 @@ import (
"sync" "sync"
"time" "time"
"github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/models" "github.com/owncast/owncast/models"
"github.com/owncast/owncast/persistence/webhookrepository"
) )
// WebhookEvent represents an event sent as a webhook. // WebhookEvent represents an event sent as a webhook.
@@ -31,7 +31,8 @@ func SendEventToWebhooks(payload WebhookEvent) {
} }
func sendEventToWebhooks(payload WebhookEvent, wg *sync.WaitGroup) { func sendEventToWebhooks(payload WebhookEvent, wg *sync.WaitGroup) {
webhooks := data.GetWebhooksForEvent(payload.Type) webhooksRepo := webhookrepository.Get()
webhooks := webhooksRepo.GetWebhooksForEvent(payload.Type)
for _, webhook := range webhooks { for _, webhook := range webhooks {
// Use wg to track the number of notifications to be sent. // 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/chat/events"
"github.com/owncast/owncast/core/data" "github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/models" "github.com/owncast/owncast/models"
"github.com/owncast/owncast/persistence/webhookrepository"
jsonpatch "gopkg.in/evanphx/json-patch.v5" jsonpatch "gopkg.in/evanphx/json-patch.v5"
) )
@@ -62,12 +63,14 @@ func TestPublicSend(t *testing.T) {
})) }))
defer svr.Close() 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 { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
defer func() { defer func() {
if err := data.DeleteWebhook(hook); err != nil { if err := webhooksRepo.DeleteWebhook(hook); err != nil {
t.Error(err) t.Error(err)
} }
}() }()
@@ -107,13 +110,15 @@ func TestRouting(t *testing.T) {
})) }))
defer svr.Close() defer svr.Close()
webhooksRepo := webhookrepository.Get()
for _, eventType := range eventTypes { 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 { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
defer func() { defer func() {
if err := data.DeleteWebhook(hook); err != nil { if err := webhooksRepo.DeleteWebhook(hook); err != nil {
t.Error(err) t.Error(err)
} }
}() }()
@@ -148,13 +153,15 @@ func TestMultiple(t *testing.T) {
})) }))
defer svr.Close() defer svr.Close()
webhooksRepo := webhookrepository.Get()
for i := 0; i < times; i++ { 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 { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
defer func() { defer func() {
if err := data.DeleteWebhook(hook); err != nil { if err := webhooksRepo.DeleteWebhook(hook); err != nil {
t.Error(err) t.Error(err)
} }
}() }()
@@ -186,14 +193,16 @@ func TestTimestamps(t *testing.T) {
})) }))
defer svr.Close() defer svr.Close()
webhooksRepo := webhookrepository.Get()
for i, eventType := range eventTypes { 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 { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
handlerIds[i] = hook handlerIds[i] = hook
defer func() { defer func() {
if err := data.DeleteWebhook(hook); err != nil { if err := webhooksRepo.DeleteWebhook(hook); err != nil {
t.Error(err) t.Error(err)
} }
}() }()
@@ -209,7 +218,7 @@ func TestTimestamps(t *testing.T) {
wg.Wait() wg.Wait()
hooks, err := data.GetWebhooks() hooks, err := webhooksRepo.GetWebhooks()
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@@ -285,12 +294,14 @@ func TestParallel(t *testing.T) {
})) }))
defer svr.Close() 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 { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
defer func() { defer func() {
if err := data.DeleteWebhook(hook); err != nil { if err := webhooksRepo.DeleteWebhook(hook); err != nil {
t.Error(err) t.Error(err)
} }
}() }()
@@ -320,13 +331,15 @@ func checkPayload(t *testing.T, eventType models.EventType, send func(), expecte
})) }))
defer svr.Close() defer svr.Close()
webhooksRepo := webhookrepository.Get()
// Subscribe to the webhook. // 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 { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
defer func() { defer func() {
if err := data.DeleteWebhook(hook); err != nil { if err := webhooksRepo.DeleteWebhook(hook); err != nil {
t.Error(err) t.Error(err)
} }
}() }()

View File

@@ -9,8 +9,8 @@ import (
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/models" "github.com/owncast/owncast/models"
"github.com/owncast/owncast/persistence/webhookrepository"
) )
// webhookWorkerPoolSize defines the number of concurrent HTTP webhook requests. // webhookWorkerPoolSize defines the number of concurrent HTTP webhook requests.
@@ -87,7 +87,8 @@ func sendWebhook(job Job) error {
defer resp.Body.Close() 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) 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 ( require (
github.com/CAFxX/httpcompression v0.0.9 github.com/CAFxX/httpcompression v0.0.9
github.com/SherClockHolmes/webpush-go v1.3.0 github.com/SherClockHolmes/webpush-go v1.4.0
github.com/TwiN/go-away v1.6.13 github.com/TwiN/go-away v1.6.14
github.com/andybalholm/cascadia v1.3.2 github.com/andybalholm/cascadia v1.3.3
github.com/aws/aws-sdk-go v1.55.5 github.com/aws/aws-sdk-go v1.55.6
github.com/go-chi/chi/v5 v5.1.0 github.com/go-chi/chi/v5 v5.2.1
github.com/go-fed/activity v1.0.1-0.20210803212804-d866ba75dd0f github.com/go-fed/activity v1.0.1-0.20220119073622-b14b50eecad0
github.com/go-fed/httpsig v1.1.0 github.com/go-fed/httpsig v1.1.0
github.com/gorilla/websocket v1.5.3 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/jellydator/ttlcache/v3 v3.3.0
github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible
github.com/mattn/go-sqlite3 v1.14.24 github.com/mattn/go-sqlite3 v1.14.24
@@ -28,18 +28,18 @@ require (
github.com/prometheus/client_golang v1.20.5 github.com/prometheus/client_golang v1.20.5
github.com/rifflock/lfshook v0.0.0-20180920164130-b9218ef580f5 github.com/rifflock/lfshook v0.0.0-20180920164130-b9218ef580f5
github.com/schollz/sqlite3dump v1.3.1 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/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/teris-io/shortid v0.0.0-20220617161101-71ec9f2aa569
github.com/yuin/goldmark v1.7.8 github.com/yuin/goldmark v1.7.8
github.com/yuin/goldmark-emoji v1.0.4 github.com/yuin/goldmark-emoji v1.0.4
golang.org/x/crypto v0.28.0 golang.org/x/crypto v0.33.0
golang.org/x/mod v0.21.0 golang.org/x/mod v0.23.0
golang.org/x/net v0.30.0 golang.org/x/net v0.35.0
golang.org/x/time v0.7.0 golang.org/x/time v0.10.0
gopkg.in/evanphx/json-patch.v5 v5.9.0 gopkg.in/evanphx/json-patch.v5 v5.9.11
mvdan.cc/xurls/v2 v2.5.0 mvdan.cc/xurls/v2 v2.6.0
) )
require ( require (
@@ -49,10 +49,10 @@ require (
github.com/beorn7/perks v1.0.1 // indirect github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/davecgh/go-spew v1.1.1 // 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-ole/go-ole v1.2.6 // indirect
github.com/go-test/deep v1.0.4 // 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/google/uuid v1.5.0 // indirect
github.com/gorilla/css v1.0.1 // indirect github.com/gorilla/css v1.0.1 // indirect
github.com/jmespath/go-jmespath v0.4.0 // 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/go-sysconf v0.3.12 // indirect
github.com/tklauser/numcpus v0.6.1 // indirect github.com/tklauser/numcpus v0.6.1 // indirect
github.com/yusufpapurcu/wmi v1.2.4 // indirect github.com/yusufpapurcu/wmi v1.2.4 // indirect
golang.org/x/sync v0.8.0 // indirect golang.org/x/sync v0.11.0 // indirect
golang.org/x/sys v0.26.0 // indirect golang.org/x/sys v0.30.0 // indirect
golang.org/x/text v0.19.0 // indirect golang.org/x/text v0.22.0 // indirect
google.golang.org/protobuf v1.34.2 // indirect google.golang.org/protobuf v1.34.2 // indirect
gopkg.in/yaml.v3 v3.0.1 // 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 h1:0ue2X8dOLEpxTm8tt+OdHcgA+gbDge0OqFQWGKSqgrg=
github.com/CAFxX/httpcompression v0.0.9/go.mod h1:XX8oPZA+4IDcfZ0A71Hz0mZsv/YJOgYygkFhizVPilM= 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/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.4.0 h1:ocnzNKWN23T9nvHi6IfyrQjkIc0oJWv1B1pULsf9i3s=
github.com/SherClockHolmes/webpush-go v1.3.0/go.mod h1:AxRHmJuYwKGG1PVgYzToik1lphQvDnqFYDqimHvwhIw= github.com/SherClockHolmes/webpush-go v1.4.0/go.mod h1:XSq8pKX11vNV8MJEMwjrlTkxhAj1zKfxmyhdV7Pd6UA=
github.com/TwiN/go-away v1.6.13 h1:aB6l/FPXmA5ds+V7I9zdhxzpsLLUvVtEuS++iU/ZmgE= github.com/TwiN/go-away v1.6.14 h1:gjFP+6/A36gmj0NpYX0Sz9hrdU0KtHwtNWYnsJgV4fo=
github.com/TwiN/go-away v1.6.13/go.mod h1:MpvIC9Li3minq+CGgbgUDvQ9tDaeW35k5IXZrF9MVas= 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 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs=
github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= 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.3 h1:AG2YHrzJIm4BZ19iwJ/DAua6Btl3IwJX+VI4kktS1LM=
github.com/andybalholm/cascadia v1.3.2/go.mod h1:7gtRlve5FxPPgIgX36uWBX58OdBsSS6lUvCFb+h7KvU= 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 h1:axGnT1gRIfimI7gJifB699GoE/oq+F2MU7Dml6nw9rQ=
github.com/apapsch/go-jsonmerge/v2 v2.0.0/go.mod h1:lvDnEdqiQrp0O42VQGgmlKpxL1AP2+08jFMw88y4klk= 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.6 h1:cSg4pvZ3m8dgYcgqB97MrcdjUmZ1BeMYKUxMMB89IPk=
github.com/aws/aws-sdk-go v1.55.5/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= 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 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=
github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4= github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= 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.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 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 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.2 h1:jPPGWs2sZ1UgOSgD2bClL0MJIqu58nOmIcBuXr62z1I=
github.com/ebitengine/purego v0.8.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= github.com/ebitengine/purego v0.8.2/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.2.1 h1:KOIHODQj58PmL80G2Eak4WdvUzjSJSm0vG72crDCqb8=
github.com/go-chi/chi/v5 v5.1.0/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= 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 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 h1:9M+hb0jkEICD8/cAiNqEB66R87tTINszBRTjwjQzWcI=
github.com/go-fed/httpsig v1.1.0/go.mod h1:RCMrTZvN1bJYtofsG4rd5NaO5obxQ5xBkdiS7xsT7bM= 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.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 h1:u2CU3YKy9I2pmu9pX0eq50wCgjfGIt539SqR7FbHiho=
github.com/go-test/deep v1.0.4/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= 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/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk=
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= 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/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 h1:jopqB+UTSdJGEJT8tEqYyE29zN91fi2827oLET8tl7k=
github.com/google/brotli/go/cbrotli v0.0.0-20230829110029-ed738e842d2f/go.mod h1:nOPhAkwVliJdNTkj3gXpljmWhjc4wCaVqbMJcPKWP4s= 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/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 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= 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.1 h1:DuP1uA1kvRRmGNAZ0m+ObLv1dvrfNO0TPx0c/enNk0s=
github.com/grafov/m3u8 v0.12.0/go.mod h1:nqzOkfBiZJENr52zTVd/Dcl03yzphIMbJqkXGu+u080= 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 h1:BdoC9cE81qXfrxeb9eoJi9dWrdhSuwXMAnHTbnBm4Wc=
github.com/jellydator/ttlcache/v3 v3.3.0/go.mod h1:bj2/e0l4jRnQdrnSTaGTsh4GSXvMjQcy41i7th0GVGw= github.com/jellydator/ttlcache/v3 v3.3.0/go.mod h1:bj2/e0l4jRnQdrnSTaGTsh4GSXvMjQcy41i7th0GVGw=
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= 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 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4=
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= 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.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 h1:tpSp2G2KyMnnQu99ngJ47EIkWVmliIizyZBfPrBWDRM=
github.com/mattn/go-sqlite3 v1.14.24/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= 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= 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/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 h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw=
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= 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 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y=
github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= 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= 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/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 h1:mZHayPoR0lNmnHyvtYjDeq0zlVHn9K/ZXoy17ylucdo=
github.com/rifflock/lfshook v0.0.0-20180920164130-b9218ef580f5/go.mod h1:GEXHk5HgEKCvEIIrSpFI3ozzG5xOKA2DVlEX/gGnewM= 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.13.2-0.20241226121412-a5dc8ff20d0a h1:w3tdWGKbLGBPtR/8/oO74W6hmz0qE5q0z9aqSAewaaM=
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/go.mod h1:S8kfXMp+yh77OxPD4fdM6YUknrZpQxLhvxzS4gDHENY=
github.com/schollz/sqlite3dump v1.3.1 h1:QXizJ7XEJ7hggjqjZ3YRtF3+javm8zKtzNByYtEkPRA= github.com/schollz/sqlite3dump v1.3.1 h1:QXizJ7XEJ7hggjqjZ3YRtF3+javm8zKtzNByYtEkPRA=
github.com/schollz/sqlite3dump v1.3.1/go.mod h1:mzSTjZpJH4zAb1FN3iNlhWPbbdyeBpOaTW0hukyMHyI= 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.25.1 h1:QSWkTc+fu9LTAWfkZwZ6j8MSUk4A2LV7rbH0ZqmLjXs=
github.com/shirou/gopsutil/v4 v4.24.9/go.mod h1:3fkaHNeYsUFCGZ8+9vZVWtbyM1k2eRnlL+bWO8Bxa/Q= 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 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= 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= 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.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.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.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.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= 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 h1:xzABM9let0HLLqFypcxvLmlvEciCHL7+Lv+4vwZqecI=
github.com/teris-io/shortid v0.0.0-20220617161101-71ec9f2aa569/go.mod h1:2Ly+NIftZN4de9zRmENdYbvPQeaVIYKWpLFStLFEBgI= 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= 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/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.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.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 h1:iERMLn0/QJeHFhxSt3p6PeN9mGnvIKSpG9YYorDMnic=
github.com/yuin/goldmark v1.7.8/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E= 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 h1:vCwMkPZSNefSUnOW2ZKRUjBSD5Ok3W78IXhGxxAEF90=
github.com/yuin/goldmark-emoji v1.0.4/go.mod h1:tTkZEbwu5wkPmgTcitqddVxY9osFZiavD+r4AzQrh1U= github.com/yuin/goldmark-emoji v1.0.4/go.mod h1:tTkZEbwu5wkPmgTcitqddVxY9osFZiavD+r4AzQrh1U=
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= 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-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-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.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.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A= golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70= golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw= golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U= 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.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.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0= golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= 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-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-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-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-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.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.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.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo= golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0= golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4= golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU= 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-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.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.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= 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-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-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 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-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.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.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.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.11.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.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.26.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-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.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.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.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.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
golang.org/x/term v0.24.0/go.mod h1:lOBK/LVxemqiMij05LGJ0tzNr8xlmwBRJ81PX6wVLH8= 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.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.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.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.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.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U= golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ= golang.org/x/time v0.10.0 h1:3usCWA8tQn0L8+hFJQNgzpWbd89begxN66o1Ojdn5L4=
golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= 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-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.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.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.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-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/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= 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 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 h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= 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.11 h1:OMPeiLomOQwe8+Ku4nwXsdOmrRw2vGUpP3XgLj3ojNw=
gopkg.in/evanphx/json-patch.v5 v5.9.0/go.mod h1:/kvTRh1TVm5wuM6OkHxqXtE/1nUZZpihg29RtuIyfvk= 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.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 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= 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.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 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 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.6.0 h1:3NTZpeTxYVWNSokW3MKeyVkz/j7uYXYiMtXRUfmjbgI=
mvdan.cc/xurls/v2 v2.5.0/go.mod h1:yQgaGQ1rFtJUzkmKiHYSSfuQxqfYmd//X6PxvholpeE= mvdan.cc/xurls/v2 v2.6.0/go.mod h1:bCvEZ1XvdA6wDnxY7jPPjEmigDtvtvPXAD/Exa9IMSk=

15
main.go
View File

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

View File

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

View File

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

View File

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

View File

@@ -1,4 +1,4 @@
package data package models
import ( import (
"bytes" "bytes"
@@ -12,48 +12,48 @@ type ConfigEntry struct {
Key string Key string
} }
func (c *ConfigEntry) getStringSlice() ([]string, error) { func (c *ConfigEntry) GetStringSlice() ([]string, error) {
decoder := c.getDecoder() decoder := c.GetDecoder()
var result []string var result []string
err := decoder.Decode(&result) err := decoder.Decode(&result)
return result, err return result, err
} }
func (c *ConfigEntry) getStringMap() (map[string]string, error) { func (c *ConfigEntry) GetStringMap() (map[string]string, error) {
decoder := c.getDecoder() decoder := c.GetDecoder()
var result map[string]string var result map[string]string
err := decoder.Decode(&result) err := decoder.Decode(&result)
return result, err return result, err
} }
func (c *ConfigEntry) getString() (string, error) { func (c *ConfigEntry) GetString() (string, error) {
decoder := c.getDecoder() decoder := c.GetDecoder()
var result string var result string
err := decoder.Decode(&result) err := decoder.Decode(&result)
return result, err return result, err
} }
func (c *ConfigEntry) getNumber() (float64, error) { func (c *ConfigEntry) GetNumber() (float64, error) {
decoder := c.getDecoder() decoder := c.GetDecoder()
var result float64 var result float64
err := decoder.Decode(&result) err := decoder.Decode(&result)
return result, err return result, err
} }
func (c *ConfigEntry) getBool() (bool, error) { func (c *ConfigEntry) GetBool() (bool, error) {
decoder := c.getDecoder() decoder := c.GetDecoder()
var result bool var result bool
err := decoder.Decode(&result) err := decoder.Decode(&result)
return result, err return result, err
} }
func (c *ConfigEntry) getObject(result interface{}) error { func (c *ConfigEntry) GetObject(result interface{}) error {
decoder := c.getDecoder() decoder := c.GetDecoder()
err := decoder.Decode(result) err := decoder.Decode(result)
return err return err
} }
func (c *ConfigEntry) getDecoder() *gob.Decoder { func (c *ConfigEntry) GetDecoder() *gob.Decoder {
valueBytes := c.Value.([]byte) valueBytes := c.Value.([]byte)
decoder := gob.NewDecoder(bytes.NewBuffer(valueBytes)) decoder := gob.NewDecoder(bytes.NewBuffer(valueBytes))
return decoder 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/core/data"
"github.com/owncast/owncast/db" "github.com/owncast/owncast/db"
"github.com/owncast/owncast/models" "github.com/owncast/owncast/models"
"github.com/owncast/owncast/persistence/configrepository"
"github.com/owncast/owncast/persistence/tables" "github.com/owncast/owncast/persistence/tables"
"github.com/owncast/owncast/notifications/browser" "github.com/owncast/owncast/notifications/browser"
@@ -18,9 +19,10 @@ import (
// Notifier is an instance of the live stream notifier. // Notifier is an instance of the live stream notifier.
type Notifier struct { type Notifier struct {
datastore *data.Datastore datastore *data.Datastore
browser *browser.Browser browser *browser.Browser
discord *discord.Discord discord *discord.Discord
configRepository configrepository.ConfigRepository
} }
// Setup will perform any pre-use setup for the notifier. // Setup will perform any pre-use setup for the notifier.
@@ -30,8 +32,10 @@ func Setup(datastore *data.Datastore) {
} }
func initializeBrowserPushIfNeeded() { func initializeBrowserPushIfNeeded() {
pubKey, _ := data.GetBrowserPushPublicKey() configRepository := configrepository.Get()
privKey, _ := data.GetBrowserPushPrivateKey()
pubKey, _ := configRepository.GetBrowserPushPublicKey()
privKey, _ := configRepository.GetBrowserPushPrivateKey()
// We need browser push keys so people can register for pushes. // We need browser push keys so people can register for pushes.
if pubKey == "" || privKey == "" { if pubKey == "" || privKey == "" {
@@ -40,26 +44,27 @@ func initializeBrowserPushIfNeeded() {
log.Errorln("unable to initialize browser push notification keys", err) 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) 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) log.Errorln("unable to set browser push public key", err)
} }
} }
// Enable browser push notifications by default. // Enable browser push notifications by default.
if !data.GetHasPerformedInitialNotificationsConfig() { if !configRepository.GetHasPerformedInitialNotificationsConfig() {
_ = data.SetBrowserPushConfig(models.BrowserNotificationConfiguration{Enabled: true, GoLiveMessage: config.GetDefaults().FederationGoLiveMessage}) _ = configRepository.SetBrowserPushConfig(models.BrowserNotificationConfiguration{Enabled: true, GoLiveMessage: config.GetDefaults().FederationGoLiveMessage})
_ = data.SetHasPerformedInitialNotificationsConfig(true) _ = configRepository.SetHasPerformedInitialNotificationsConfig(true)
} }
} }
// New creates a new instance of the Notifier. // New creates a new instance of the Notifier.
func New(datastore *data.Datastore) (*Notifier, error) { func New(datastore *data.Datastore) (*Notifier, error) {
notifier := Notifier{ notifier := Notifier{
datastore: datastore, datastore: datastore,
configRepository: configrepository.Get(),
} }
if err := notifier.setupBrowserPush(); err != nil { if err := notifier.setupBrowserPush(); err != nil {
@@ -73,13 +78,13 @@ func New(datastore *data.Datastore) (*Notifier, error) {
} }
func (n *Notifier) setupBrowserPush() error { func (n *Notifier) setupBrowserPush() error {
if data.GetBrowserPushConfig().Enabled { if n.configRepository.GetBrowserPushConfig().Enabled {
publicKey, err := data.GetBrowserPushPublicKey() publicKey, err := n.configRepository.GetBrowserPushPublicKey()
if err != nil || publicKey == "" { if err != nil || publicKey == "" {
return errors.Wrap(err, "browser notifier disabled, failed to get browser push public key") 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 == "" { if err != nil || privateKey == "" {
return errors.Wrap(err, "browser notifier disabled, failed to get browser push private key") 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) log.Errorln("error getting browser push notification destinations", err)
} }
for _, destination := range destinations { 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 unsubscribed {
// If the error is "unsubscribed", then remove the destination from the database. // If the error is "unsubscribed", then remove the destination from the database.
if err := RemoveNotificationForChannel(BrowserPushNotification, destination); err != nil { if err := RemoveNotificationForChannel(BrowserPushNotification, destination); err != nil {
@@ -112,14 +117,14 @@ func (n *Notifier) notifyBrowserPush() {
} }
func (n *Notifier) setupDiscord() error { func (n *Notifier) setupDiscord() error {
discordConfig := data.GetDiscordConfig() discordConfig := n.configRepository.GetDiscordConfig()
if discordConfig.Enabled && discordConfig.Webhook != "" { if discordConfig.Enabled && discordConfig.Webhook != "" {
var image string var image string
if serverURL := data.GetServerURL(); serverURL != "" { if serverURL := n.configRepository.GetServerURL(); serverURL != "" {
image = serverURL + "/logo" image = serverURL + "/logo"
} }
discordNotifier, err := discord.New( discordNotifier, err := discord.New(
data.GetServerName(), n.configRepository.GetServerName(),
image, image,
discordConfig.Webhook, discordConfig.Webhook,
) )
@@ -132,12 +137,12 @@ func (n *Notifier) setupDiscord() error {
} }
func (n *Notifier) notifyDiscord() { func (n *Notifier) notifyDiscord() {
goLiveMessage := data.GetDiscordConfig().GoLiveMessage goLiveMessage := n.configRepository.GetDiscordConfig().GoLiveMessage
streamTitle := data.GetStreamTitle() streamTitle := n.configRepository.GetStreamTitle()
if streamTitle != "" { if streamTitle != "" {
goLiveMessage += "\n" + 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 { if err := n.discord.Send(message); err != nil {
log.Errorln("error sending discord message", err) log.Errorln("error sending discord message", err)
@@ -158,6 +163,7 @@ func (n *Notifier) Notify() {
// RemoveNotificationForChannel removes a notification destination. // RemoveNotificationForChannel removes a notification destination.
func RemoveNotificationForChannel(channel, destination string) error { func RemoveNotificationForChannel(channel, destination string) error {
log.Debugln("Removing notification for channel", channel) log.Debugln("Removing notification for channel", channel)
return data.GetDatastore().GetQueries().RemoveNotificationDestinationForChannel(context.Background(), db.RemoveNotificationDestinationForChannelParams{ return data.GetDatastore().GetQueries().RemoveNotificationDestinationForChannel(context.Background(), db.RemoveNotificationDestinationForChannelParams{
Channel: channel, Channel: channel,
Destination: destination, Destination: destination,

View File

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