1
0
Fork 0

v7.0.0-rc.1

-----BEGIN PGP SIGNATURE-----
 
 iQEzBAABCAAdFiEExEqtY4NnkSypPt1XWDphLYkBWb4FAmdHE0IACgkQWDphLYkB
 Wb4dDQf+NiWupO0EsB/pgfSzcSDsnbTL6T0C+ZVFO9PU6VgU+yF9AHAXQuh/7PzE
 CPq+4LXbYH0KBpSOsjy7FylmsEt6LOpb1TpfqBgQncEWXVvI4jYcbI37Uxgi2cnd
 4nDCmBhJ3m5ZzO+fjGQwlJgkRX/On+ybj8U22BiCgeMd1pIxNXxU/UCK5nR0SgE2
 Bw84+vefolRqs0eGYxh+iSP8tL/VlAjbxvNv0d7j4ed9RcFdcEDZr6clfdaIBjer
 ud7r+LfNSUkxLxN/mqDFfDcy89SETTUip4X4iE2Cv5pDlqhYpsF4udIbtMVTUQ7I
 OrreJHrMY/cuBQK/4eqsDYuE+NErCQ==
 =aXYl
 -----END PGP SIGNATURE-----
gpgsig -----BEGIN PGP SIGNATURE-----
 
 iQJLBAABCAA1FiEEGCBYi9NGimfngx9dVTwOu+tdXwgFAmeSonwXHGdpdEBrb3Rv
 dmFsZXhhcmlhbi5jb20ACgkQVTwOu+tdXwiRqBAAsYy5MoUZrj/g1+tPxyhYir3/
 Cflf5eYpp6WcXaItL7Lx8mFbhendGaAfDOisPufNOZVobJxY+Mz2rpH8uymZ6hjR
 CE5j7sQQtqXsCjtQqubcZxtxlgpo73CBukCAuwYdNOym/PmWy3tWt2rhpcnzHbVY
 nBuSU69nHOjlKx043th/m+W6XwaXBZbZqgOKng8cp704vyWMFHbJiVd8fwDJeg+R
 74e2zXAw8pnQnjXdPHcsCbPu4BFpcfXl/btJifQ75fK1VxPcD4kGjoAQuG6XtzCs
 +yNQhcw52wNmee1Ns+I93/b4IuwwwP1f3908UOyAzgwgns+cUEPB2AFBNGsn3S9x
 psY+N1zZKmjvFeV4jpVId3MxEACXIGPFr8KmZbqKjQtlB+Y6vZ3kL40wFrZAhFQi
 ArSQSq3BDW3FjMleX/gyw82ZGuVhvU6ynEC481Nd9D60NYO0q3yNStjoFTF4fSKd
 g8SB/5vnoPaJ7OZkDN+WfQBlcZrCgyXpddZjrMIa37NaHRsL4DAFObcrXYNvyDC2
 qxGY31pKrNyh3TJOqU/Dfu+zRD9H+/E7DnZaN66NSxYlXwxUwDIfLSiyyD220bPT
 PloAcFj8ZxT0nFRdaTr+6tqeeHsTEjnfhQv0qEuQh0h9AnqDy3ht/Y3aDF65sjaw
 qEF2hWlrQlVbeYxHI+E=
 =Yk3i
 -----END PGP SIGNATURE-----

Merge tag 'v7.0.0-rc.1' into changes

v7.0.0-rc.1
This commit is contained in:
Alex Kotov 2025-01-24 00:01:29 +04:00
commit a955dcb690
922 changed files with 179292 additions and 232953 deletions

View file

@ -1,39 +1,10 @@
# Welcome to the contributing guide for PeerTube
# PeerTube Contributing Guide
Interested in contributing? Awesome!
Welcome to the contributing guide for PeerTube! Interested in contributing? Awesome!
**This guide will present you the following contribution topics:**
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
- [Translate](#translate)
- [Give your feedback](#give-your-feedback)
- [Write documentation](#write-documentation)
- [Improve the website](#improve-the-website)
- [Develop](#develop)
- [Prerequisites](#prerequisites)
- [Online development](#online-development)
- [Server side](#server-side)
- [Client side](#client-side)
- [Client and server side](#client-and-server-side)
- [Embed](#embed)
- [RTL layout](#rtl-layout)
- [Testing](#testing)
- [Unit/integration tests](#unitintegration-tests)
- [Play with a federation of PeerTube servers](#play-with-a-federation-of-peertube-servers)
- [Emails](#emails)
- [OpenAPI documentation](#openapi-documentation)
- [Environment variables](#environment-variables)
- [Generate/pull translations](#generatepull-translations)
- [Release PeerTube](#release-peertube)
- [PeerTube packages](#peertube-packages)
- [CI](#ci)
- [Monitoring](#monitoring)
- [Test live stream](#test-live-stream)
- [Plugins & Themes](#plugins--themes)
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
[[toc]]
## Translate
@ -64,7 +35,7 @@ You can update it by writing markdown files in the following repository: https:/
The [REST API documentation](https://docs.joinpeertube.org/api-rest-reference.html) is generated from `support/doc/api/openapi.yaml` file.
To quickly get a preview of your changes, you can generate the documentation *on the fly* using the following command:
```
```sh
npx @redocly/cli preview-docs ./support/doc/api/openapi.yaml
```
@ -85,6 +56,9 @@ It is not hosted on GitHub but on [Framasoft](https://framasoft.org/)'s own [Git
## Develop
> [!TIP]
> In dev mode, administrator username is **root** and password is **test**
Always talk about features you want to develop by creating/finding and commenting the issue tackling your problem
before you start working on it, and inform the community that you begin coding by claiming the issue.
@ -96,13 +70,12 @@ link your PR to the issues it solves by using the GitHub syntax: "fixes #issue_n
First, you should use a server or PC with at least 4GB of RAM. Less RAM may lead to crashes.
1) Make sure that you have followed
[the steps](/support/doc/dependencies.md)
to install the dependencies.
1) Make sure that you have followed [the steps](/support/doc/dependencies.md) to install the dependencies.
1) Install [parallel](https://www.gnu.org/software/parallel/) to be able to run tests.
1) Fork the GitHub repository.
1) Run the following commands.
```
```sh
git clone https://github.com/Chocobozzz/PeerTube
cd PeerTube
git remote add me git@github.com:YOUR_GITHUB_USERNAME/PeerTube.git
@ -115,7 +88,8 @@ the `yarn install --pure-lockfile` command.
When you create a new branch you should also tell to use your repo for upload
not default one. To do just do:
```
```sh
git push --set-upstream me <your branch name>
```
@ -124,7 +98,7 @@ Then, create a postgres database and user with the values set in the
there, the following commands would create a new database called `peertube_dev`
and a postgres user called `peertube` with password `peertube`:
```
```sh
# sudo -u postgres createuser -P peertube
Enter password for new role: peertube
# sudo -u postgres createdb -O peertube peertube_dev
@ -132,7 +106,7 @@ Enter password for new role: peertube
Then enable extensions PeerTube needs:
```
```sh
sudo -u postgres psql -c "CREATE EXTENSION pg_trgm;" peertube_dev
sudo -u postgres psql -c "CREATE EXTENSION unaccent;" peertube_dev
```
@ -140,8 +114,6 @@ sudo -u postgres psql -c "CREATE EXTENSION unaccent;" peertube_dev
PeerTube also requires a running redis server, no special setup is needed for
this.
In dev mode, administrator username is **root** and password is **test**.
### Online development
You can get a complete PeerTube development setup with Gitpod, a free one-click online IDE for GitHub:
@ -152,7 +124,7 @@ You can get a complete PeerTube development setup with Gitpod, a free one-click
To develop on the server-side:
```
```sh
npm run dev:server
```
@ -168,7 +140,7 @@ More detailed documentation is available:
To develop on the client side:
```
```sh
npm run dev:client
```
@ -186,7 +158,7 @@ The API will listen on `localhost:9000` and the frontend on `localhost:3000`.
File changes are automatically recompiled, injected in the web browser (no need to refresh manually)
and the web server is automatically restarted.
```
```sh
npm run dev
```
@ -196,7 +168,7 @@ The embed is a standalone application built using Vite.
The generated files (HTML entrypoint and multiple JS and CSS files) are served by the Vite server (behind `localhost:5173/videos/embed/:videoUUID` or `localhost:5173/video-playlists/embed/:playlistUUID`).
The following command will compile embed files and run the PeerTube server:
```
```sh
npm run dev:embed
```
@ -204,7 +176,7 @@ npm run dev:embed
To test RTL (right-to-left) layout using `ar` locale:
```
```sh
npm run dev -- --ar-locale
```
@ -222,21 +194,21 @@ See the [dedicated documentation](/support/doc/development/tests.md) to run test
Create a PostgreSQL user **with the same name as your username** in order to avoid using the *postgres* user.
Then, we can create the databases (if they don't already exist):
```
```sh
sudo -u postgres createuser you_username --createdb --superuser
createdb -O peertube peertube_test{1,2,3}
```
Build the application and flush the old tests data:
```
```sh
npm run build
npm run clean:server:test
```
To run 3 nodes:
```
```sh
NODE_APP_INSTANCE=1 NODE_ENV=test npm start
NODE_APP_INSTANCE=2 NODE_ENV=test npm start
NODE_APP_INSTANCE=3 NODE_ENV=test npm start

View file

@ -1,5 +1,7 @@
name: Benchmark
permissions: {}
on:
push:
branches:

View file

@ -11,6 +11,8 @@
#
name: "CodeQL"
permissions: {}
on:
push:
branches: [ develop, next ]

View file

@ -1,5 +1,7 @@
name: Docker
permissions: {}
on:
push:
branches:

View file

@ -1,5 +1,7 @@
name: Nightly
permissions: {}
on:
schedule:
- cron: '0 3 * * *'

View file

@ -1,5 +1,7 @@
name: Stats
permissions: {}
on:
push:
branches:

View file

@ -1,5 +1,7 @@
name: Test
permissions: {}
on:
push:
pull_request:

View file

@ -1,5 +1,83 @@
# Changelog
## v7.0.0-rc.1
### IMPORTANT NOTES
* Ensure you have `storage.original_video_files` set in your configuration file: https://github.com/Chocobozzz/PeerTube/blob/develop/config/production.yaml.example#L159.
If you did not configure this key but have already enabled "Keep a version of the input file" configuration, original files may have been saved in `versions/peertube-v6.x.x/storage/original-video-files/` directories. If this is the case, you must move these files in the new directory location specified by your `storage.original_video_files` configuration
* Safari desktop versions < 13 are not supported anymore
* iOS versions < 14.5 are not supported anymore
* PeerTube instance requires python >= 3.8 for transcription
### Docker
* Fix private IPv6 subnet (we used a subnet reserved for examples)
### Plugins/Themes/Embed API
* Remove client plugin hooks: `filter:api.recently-added-videos.videos.list.{params,result}`, `filter:api.local-videos.videos.list.{params,result}`, `filter:api.trending-videos.videos.list.{params,result}` `filter:api.trending-videos.videos.list.result` in favour of `filter:api.browse-videos.videos.list.{params,result}`
* Header logo doesn't have the `.icon` class anymore (it still has the `icon-logo` class)
* All CSS variables have been replaced so it's easier to theme PeerTube:
* PeerTube generates a color palette based on a few main colors (`primary`, `fg`, `bg`, `bg-secondary`...): https://github.com/Chocobozzz/PeerTube/blob/develop/client/src/sass/application.scss#L27
* Some new variables fallback to old variables to limit theme breaks
### Admin config (non-exhaustive)
* Add ability to configure STUN servers IPs: `webrtc.stun_servers`
* Remove `client.videos.miniature.display_author_avatar` config: author avatars are now always displayed
### Features
* :tada: Global client redesign :tada:
* Introduce a new *Light/Beige* theme that replaces the current one (black/orange)
* Add a *Dark/Brown* theme directly in PeerTube core
* Split *My library* pages into:
* *Video Space* pages (that contains account channels, videos...)
* *My library* pages (that contains account playlists, subscriptions...)
* Split *Administration* pages into:
* *Overview* pages (to list instance users, videos...)
* *Moderation* pages (to list abuses, blocks, registrations...)
* *Settings* pages (instance configuration, list runners...)
* Reorganize the header and the left menu:
* Account settings and notifications are now in the header
* Add instance name and description in the left menu for anonymous users
* Merge *Recently Added* and *Trending* and *Local videos* videos pages into a *Browse videos* page that includes quick filters
* Improve *Discover videos* page UX
* Redesign the left menu, the horizontal menus, form controls, buttons and video filters panel
* Replace/remove/add some icons
* :tada: Introduce a modal to easily add/edit/remove subtitle segments :tada:
* Improve accessibility:
* Fix contrast issues
* Add missing labels
* Fix progress bar, custom select components, tag input components, notification component accessibility
* Add underlining to links
* Add "skip menu" links
* Improve keyboard navigation
* Fix various screen readers issues
* SEO:
* Add instance avatar to OpenGraph tags
* Hide empty accounts/channels from sitemap [#6633](https://github.com/Chocobozzz/PeerTube/pull/6633)
* Inject additional video tags to sitemap [#6633](https://github.com/Chocobozzz/PeerTube/pull/6633)
* Various UX improvements:
* Improve player control bar responsive
* Add refresh button to following list
* Clearer signup limit label
### Bug fixes
* Fix *My channel* search
* Fix channel sync edition/listing
* Fix adding video tags on Android
* Fix fetching client comment URL using ActivityPub resolver (Mastodon search bar...)
* Fix crash when logging SQL requests and enabled prettify option
* Correctly delete web videos with hls without audio
* Fix auto blacklisting unlisted videos
* Fix *ERR_BUFFER_OUT_OF_BOUNDS* error on some node version
* Add ability to set max channel sync in admin config
## v6.3.3
### Bug fixes

View file

@ -73,7 +73,7 @@ Project maintainers may further define and clarify representation of a project.
## Enforcement
You should report any instances of abusive, harassing, or otherwise unacceptable behaviour to the project team at chocobozzz@framasoft.org. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain the anonymity of the reporter of an incident. We may post further details of specific enforcement policies separately.
You should report any instances of abusive, harassing, or otherwise unacceptable behaviour to the project team at peertube@framasoft.org. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain the anonymity of the reporter of an incident. We may post further details of specific enforcement policies separately.
Project maintainers who do not follow or enforce this code of conduct in good faith may face temporary or permanent consequences. These will be determined by members of the project's leadership.

View file

@ -5,8 +5,8 @@
* DignifiedSilence
* Александр
* T.S
* Hồ Nhất Duy
* josé m
* Hồ Nhất Duy
* Jeff Huang
* Ihor Hordiichuk
* Filip Bengtsson
@ -36,10 +36,10 @@
* John Livingston
* Hannes Ylä-Jääski
* Kim
* Besnik Bleta
* Vodoyo Kamal
* Armin
* Fontan 030
* Besnik Bleta
* Mohamad Reza
* Quentin PAGÈS
* Kimsible
@ -56,12 +56,12 @@
* Julien Maulny
* Mark Van den Borre
* x
* Manuel Viens
* Jorropo
* Josh Morel
* Manuel Viens
* Renne Rocha
* BO41
* Ettore Atalan
* Renne Rocha
* vachan
* Elegant Codes
* Florian CUNY
@ -78,6 +78,7 @@
* Ch
* J. Lavoie
* YILDIRIM YAPRAK
* alex gabilondo
* barzofarev2
* jan Seli
* 李奕寯
@ -92,9 +93,9 @@
* Echo Kilo
* Erik Guldberg
* Jan Keromnes
* Jiří Podhorecký
* Luc Didry
* Siourdakis Thanos
* alex gabilondo
* knuxify
* Agron Selimaj
* Attila F
@ -102,6 +103,7 @@
* David Soh
* Diazepan Medina
* Jason Zhou
* Kerim Demirkaynak
* Loukas Stamellos
* Ms Kimsible
* NorbiPeti
@ -115,6 +117,7 @@
* Lucas Declercq
* Sirxy
* matograine
* Adrià Martín
* Ahmed ABERWAG
* Daniel Santos
* David Libeau
@ -132,7 +135,6 @@
* nexi
* owiox8+1viroxeaziaxw@sharklasers.com
* yns bag
* Adrià Martín
* Anne-Gaelle Moulun
* Arman
* Asier Iturralde Sarasola
@ -146,7 +148,6 @@
* Green-Star
* I_Automne
* Ilia
* Kerim Demirkaynak
* Micah Elizabeth Scott
* Pierre-Jean
* Ret Samys
@ -159,6 +160,7 @@
* boris joeson
* frankstrater
* mater
* spf
* test2a
* think4web
* 路过是好事
@ -171,9 +173,11 @@
* Cokelat8
* DontUseGithub
* Farooq Karimi Zadeh
* Frederic Bezies
* Iñigo
* Jim Kats
* Joan Montané
* José M
* Kristoffer Grundström
* LecygneNoir
* Lukas
@ -195,7 +199,6 @@
* helabasa
* kaiyou
* roberto marcolin
* spf
* Ahsan Haris Ahmed
* Alberto Teira
* Alejandro
@ -230,6 +233,7 @@
* Kiro
* Leopere
* Linus
* Lionel Caylat
* Lukas Winkler
* M Z
* Manuela Silva
@ -292,13 +296,14 @@
* HHY
* Hange
* Hjalte
* HugeFrog24
* Hugo Peixoto
* HybridGlucose
* J C Worm
* Jan Ainali
* Jan Hartig
* Jan Marsalek
* Jerguš Fonfer
* José M
* Joël Galeran
* Julien Lemaire
* Julien Rabier
@ -309,11 +314,13 @@
* Mondo Xíbaro
* Moritz Warning
* Mostafa Ahangarha
* Murat Özalp
* Neko Nekowazarashi
* Nicolai Larsen
* Nojus
* Olivier Bouillet
* Pierre Jaury
* Piotr Strębski
* Puryx
* Quentin
* ROBERT MCDOWELL
@ -396,8 +403,10 @@
* Clément Brizard
* DLP
* Daniel Dutra
* David Baumgold
* David Dobryakov
* DeeJayBro
* Deval
* Dimitri DI GUSTO
* Dimitrios Glentadakis
* Durgaraj Karki
@ -455,6 +464,7 @@
* JustAnotherArchivist
* Kent Anderson
* Kevin Cope
* Kevin Pliester
* Knackie
* Kody
* Konstantinos Agiannis
@ -490,7 +500,6 @@
* Mikel Gartzia Santamaria
* Milo van der Linden
* MrGiga
* Murat Özalp
* Mélanie Pin
* Nataly Rocha
* Nathanaël J
@ -558,6 +567,7 @@
* Vagelis F
* Varik Valefor
* Vegard Fjeldberg
* VeryREAL
* Vik
* Vincent Stakenburg
* WhiredPlanck

View file

@ -26,33 +26,19 @@ Be part of a network of multiple small federated, interoperable video hosting pr
</a>
</p>
<p align="center">
<strong>Client</strong>
<div align="center">
<a href="https://github.com/Chocobozzz/PeerTube/actions?query=workflow%3A%22Test%22+branch%3Adevelop">
<img src="https://github.com/Chocobozzz/PeerTube/workflows/Test/badge.svg" alt="GitHub Test Status">
</a>
<br />
<a href="https://automate.browserstack.com/public-build/ZEZqamJQUXFQd1l3cFp3QmxLSVVwdjBGZjNGc3J2M09INFpka296em9VYz0tLUowWVdoemxkY1hBOU9aZzNlY1htZ3c9PQ==--68e0184ce76481d36559d681d9cddc68235ff536">
<img src="https://automate.browserstack.com/badge.svg?badge_key=ZEZqamJQUXFQd1l3cFp3QmxLSVVwdjBGZjNGc3J2M09INFpka296em9VYz0tLUowWVdoemxkY1hBOU9aZzNlY1htZ3c9PQ==--68e0184ce76481d36559d681d9cddc68235ff536"/>
<a href="https://automate.browserstack.com/public-build/d0ZMeGpSUFRjaUpDNnN3NUdKY1l2TnNpTGVJaUI0bm9hYkNxMTRtQ1lHTT0tLVZQa2crbFB1c2RDZUl1Y1Blck02SFE9PQ==--5f956d6857c50e06a0b7b1fe405fb93d0f2d0e11%">
<img src="https://automate.browserstack.com/badge.svg?badge_key=d0ZMeGpSUFRjaUpDNnN3NUdKY1l2TnNpTGVJaUI0bm9hYkNxMTRtQ1lHTT0tLVZQa2crbFB1c2RDZUl1Y1Blck02SFE9PQ==--5f956d6857c50e06a0b7b1fe405fb93d0f2d0e11%" alt="BrowserStack Status">
</a>
<a href="https://weblate.framasoft.org/projects/peertube/angular/">
<img src="https://weblate.framasoft.org/widgets/peertube/-/angular/svg-badge.svg"/>
<img src="https://weblate.framasoft.org/widgets/peertube/-/angular/svg-badge.svg" alt="Weblate Status">
</a>
</p>
<p align="center">
<strong>Server</strong>
<br />
<a href="https://github.com/Chocobozzz/PeerTube/actions?query=workflow%3A%22Test+Suite%22+branch%3Adevelop">
<img alt="test suite status" src="https://github.com/Chocobozzz/PeerTube/workflows/Test%20Suite/badge.svg" />
</a>
<a href="https://standardjs.com/">
<img src="https://img.shields.io/badge/code%20style-standard-brightgreen.svg" alt="JavaScript Style Guide" />
</a>
</p>
</div>
<br />
@ -130,7 +116,7 @@ Content creators can get help from their viewers in the simplest way possible: a
You don't need to be a programmer to help!
You can give us your feedback, report bugs, help us translate PeerTube, write documentation, and more. Check out the [contributing
guide](https://github.com/Chocobozzz/PeerTube/blob/develop/.github/CONTRIBUTING.md) to know how, it takes less than 2 minutes to get started. :wink:
guide](https://docs.joinpeertube.org/contribute/getting-started) to know how, it takes less than 2 minutes to get started. :wink:
You can also join the cheerful bunch that makes our community:

View file

@ -36,9 +36,9 @@ cli-table3@^0.6.0:
"@colors/colors" "1.5.0"
cross-spawn@^6.0.0:
version "6.0.5"
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4"
integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==
version "6.0.6"
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.6.tgz#30d0efa0712ddb7eb5a76e1e8721bffafa6b5d57"
integrity sha512-VqCUuhcd1iB+dsv8gxPttb5iZh/D0iubSP21g36KXdEuf6I5JiioesUVjpCdHV9MZRUfVFlvwtIUyPfxo5trtw==
dependencies:
nice-try "^1.0.4"
path-key "^2.0.1"
@ -174,9 +174,9 @@ signal-exit@^3.0.0, signal-exit@^3.0.2:
integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==
sort-keys@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/sort-keys/-/sort-keys-5.0.0.tgz#5d775f8ae93ecc29bc7312bbf3acac4e36e3c446"
integrity sha512-Pdz01AvCAottHTPQGzndktFNdbRA75BgOfeT1hH+AMnJFv8lynkPi42rfeEhpx1saTEI3YNMWxfqu0sFD1G8pw==
version "5.1.0"
resolved "https://registry.yarnpkg.com/sort-keys/-/sort-keys-5.1.0.tgz#50a3f3d1ad3c5a76d043e0aeeba7299241e9aa5c"
integrity sha512-aSbHV0DaBcr7u0PVHXzM6NbZNAtrr9sF6+Qfs9UUVG7Ll3jQ6hHi8F/xqIIcn2rvIVbr0v/2zyjSdwSV47AgLQ==
dependencies:
is-plain-obj "^4.0.0"

View file

@ -1,6 +1,6 @@
{
"name": "@peertube/peertube-runner",
"version": "0.0.21",
"version": "0.0.22",
"type": "module",
"main": "dist/peertube-runner.js",
"bin": "dist/peertube-runner.js",

View file

@ -26,18 +26,20 @@ export class RunnerServer {
private cleaningUp = false
private initialized = false
private readonly enabledJobsArray: RunnerJobType[]
private readonly sockets = new Map<PeerTubeServer, Socket>()
constructor (private readonly enabledJobs?: Set<RunnerJobType>) {}
constructor (private readonly enabledJobs?: Set<RunnerJobType>) {
this.enabledJobsArray = enabledJobs
? Array.from(enabledJobs)
: getSupportedJobsList()
}
async run () {
logger.info('Running PeerTube runner in server mode')
const enabledJobsArray = this.enabledJobs
? Array.from(this.enabledJobs)
: getSupportedJobsList()
logger.info('Supported and enabled job types: ' + enabledJobsArray.join(', '))
logger.info('Supported and enabled job types: ' + this.enabledJobsArray.join(', '))
await ConfigManager.Instance.load()
@ -237,8 +239,15 @@ export class RunnerServer {
private async requestJob (server: PeerTubeServer) {
logger.debug(`Requesting jobs on ${server.url} for runner ${server.runnerName}`)
const { availableJobs } = await server.runnerJobs.request({ runnerToken: server.runnerToken })
const { availableJobs } = await server.runnerJobs.request({
runnerToken: server.runnerToken,
jobTypes: this.enabledJobsArray.length !== getSupportedJobsList().length
? this.enabledJobsArray
: undefined
})
// FIXME: remove in PeerTube v8: jobTypes has been introduced in PeerTube v7, so do the filter here too
const filtered = availableJobs.filter(j => isJobSupported(j, this.enabledJobs))
if (filtered.length === 0) {
@ -250,7 +259,12 @@ export class RunnerServer {
}
private async tryToExecuteJobAsync (server: PeerTubeServer, jobToAccept: { uuid: string }) {
if (!this.canProcessMoreJobs()) return
if (!this.canProcessMoreJobs()) {
logger.info(
`Do not process more jobs (processing ${this.processingJobs.length} / ${ConfigManager.Instance.getConfig().jobs.concurrency})`
)
return
}
const { job } = await server.runnerJobs.accept({ runnerToken: server.runnerToken, jobUUID: jobToAccept.uuid })

View file

@ -46,5 +46,5 @@ export function isJobSupported (job: { type: RunnerJobType, payload: RunnerJobPa
}
export function getSupportedJobsList () {
return Object.keys(supportedMatrix)
return Object.keys(supportedMatrix) as unknown as RunnerJobType[]
}

View file

@ -1,7 +1,7 @@
import { pino } from 'pino'
import pretty from 'pino-pretty'
const logger = pino(pretty.default({
const logger = pino(pretty({
colorize: true
}))

View file

@ -53,11 +53,11 @@
"@types/node" "*"
"@types/node@*":
version "20.14.7"
resolved "https://registry.yarnpkg.com/@types/node/-/node-20.14.7.tgz#342cada27f97509eb8eb2dbc003edf21ce8ab5a8"
integrity sha512-uTr2m2IbJJucF3KUxgnGOZvYbN0QgkGyWxG6973HCpMYFy2KfcgYuIwkJQMQkt1VbBMlvWRbpshFTLxnxCZjKQ==
version "22.9.3"
resolved "https://registry.yarnpkg.com/@types/node/-/node-22.9.3.tgz#08f3d64b3bc6d74b162d36f60213e8a6704ef2b4"
integrity sha512-F3u1fs/fce3FFk+DAxbxc78DF8x0cY09RRL8GnXLmkJ1jvx3TtPdWoTT5/NiYfI5ASqXBmfqJi9dZ3gxMx4lzw==
dependencies:
undici-types "~5.26.4"
undici-types "~6.19.8"
abort-controller@^3.0.0:
version "3.0.0"
@ -142,9 +142,9 @@ fast-zlib@^2.0.1:
integrity sha512-DCoYgNagM2Bt1VIpXpdGnRx4LzqJeYG0oh6Nf/7cWo6elTXkFGMw9CrRCYYUIapYNrozYMoyDRflx9mgT3Awyw==
follow-redirects@^1.15.5:
version "1.15.6"
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.6.tgz#7f815c0cda4249c74ff09e95ef97c23b5fd0399b"
integrity sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==
version "1.15.9"
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.9.tgz#a604fa10e443bf98ca94228d9eebcc2e8a2c8ee1"
integrity sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==
help-me@^5.0.0:
version "5.0.0"
@ -181,9 +181,9 @@ msgpackr-extract@^3.0.2:
"@msgpackr-extract/msgpackr-extract-win32-x64" "3.0.3"
msgpackr@^1.3.2:
version "1.10.2"
resolved "https://registry.yarnpkg.com/msgpackr/-/msgpackr-1.10.2.tgz#a73de4767f76659e8c69cf9c80fdfce83937a44a"
integrity sha512-L60rsPynBvNE+8BWipKKZ9jHcSGbtyJYIwjRq0VrIvQ08cRjntGXJYW/tmciZ2IHWIY8WEW32Qa2xbh5+SKBZA==
version "1.11.2"
resolved "https://registry.yarnpkg.com/msgpackr/-/msgpackr-1.11.2.tgz#4463b7f7d68f2e24865c395664973562ad24473d"
integrity sha512-F9UngXRlPyWCDEASDpTf6c9uNhGPTqnTeLVt7bN+bU1eajoR/8V9ys2BRaV5C/e5ihE6sJ9uPIKaYt6bFuO32g==
optionalDependencies:
msgpackr-extract "^3.0.2"
@ -206,18 +206,17 @@ once@^1.3.1, once@^1.4.0:
dependencies:
wrappy "1"
pino-abstract-transport@^1.0.0, pino-abstract-transport@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/pino-abstract-transport/-/pino-abstract-transport-1.2.0.tgz#97f9f2631931e242da531b5c66d3079c12c9d1b5"
integrity sha512-Guhh8EZfPCfH+PMXAb6rKOjGQEoy0xlAIn+irODG5kgfYV+BQ0rGYYWTIel3P5mmyXqkYkPmdIkywsn6QKUR1Q==
pino-abstract-transport@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/pino-abstract-transport/-/pino-abstract-transport-2.0.0.tgz#de241578406ac7b8a33ce0d77ae6e8a0b3b68a60"
integrity sha512-F63x5tizV6WCh4R6RHyi2Ml+M70DNRXt/+HANowMflpgGFMAym/VKm6G7ZOQRjqN7XbGxK1Lg9t6ZrtzOaivMw==
dependencies:
readable-stream "^4.0.0"
split2 "^4.0.0"
pino-pretty@^11.2.1:
version "11.2.1"
resolved "https://registry.yarnpkg.com/pino-pretty/-/pino-pretty-11.2.1.tgz#de9a42ff8ea7b26da93506bb9e49d0b566c5ae96"
integrity sha512-O05NuD9tkRasFRWVaF/uHLOvoRDFD7tb5VMertr78rbsYFjYp48Vg3477EshVAF5eZaEw+OpDl/tu+B0R5o+7g==
version "11.3.0"
resolved "https://registry.yarnpkg.com/pino-pretty/-/pino-pretty-11.3.0.tgz#390b3be044cf3d2e9192c7d19d44f6b690468f2e"
integrity sha512-oXwn7ICywaZPHmu3epHGU2oJX4nPmKvHvB/bwrJHlGcbEWaVcotkpyVHMKLKmiVryWYByNp0jpgAcXpFJDXJzA==
dependencies:
colorette "^2.0.7"
dateformat "^4.6.3"
@ -227,7 +226,7 @@ pino-pretty@^11.2.1:
joycon "^3.1.1"
minimist "^1.2.6"
on-exit-leak-free "^2.1.0"
pino-abstract-transport "^1.0.0"
pino-abstract-transport "^2.0.0"
pump "^3.0.0"
readable-stream "^4.0.0"
secure-json-parse "^2.4.0"
@ -240,26 +239,26 @@ pino-std-serializers@^7.0.0:
integrity sha512-e906FRY0+tV27iq4juKzSYPbUj2do2X2JX4EzSca1631EB2QJQUqGbDuERal7LCtOpxl6x3+nvo9NPZcmjkiFA==
pino@^9.2.0:
version "9.2.0"
resolved "https://registry.yarnpkg.com/pino/-/pino-9.2.0.tgz#e77a9516f3a3e5550d9b76d9f65ac6118ef02bdd"
integrity sha512-g3/hpwfujK5a4oVbaefoJxezLzsDgLcNJeITvC6yrfwYeT9la+edCK42j5QpEQSQCZgTKapXvnQIdgZwvRaZug==
version "9.5.0"
resolved "https://registry.yarnpkg.com/pino/-/pino-9.5.0.tgz#a7ef0fea868d22d52d8a4ce46e6e03c5dc46fdd6"
integrity sha512-xSEmD4pLnV54t0NOUN16yCl7RIB1c5UUOse5HSyEXtBp+FgFQyPeDutc+Q2ZO7/22vImV7VfEjH/1zV2QuqvYw==
dependencies:
atomic-sleep "^1.0.0"
fast-redact "^3.1.1"
on-exit-leak-free "^2.1.0"
pino-abstract-transport "^1.2.0"
pino-abstract-transport "^2.0.0"
pino-std-serializers "^7.0.0"
process-warning "^3.0.0"
process-warning "^4.0.0"
quick-format-unescaped "^4.0.3"
real-require "^0.2.0"
safe-stable-stringify "^2.3.1"
sonic-boom "^4.0.1"
thread-stream "^3.0.0"
process-warning@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/process-warning/-/process-warning-3.0.0.tgz#96e5b88884187a1dce6f5c3166d611132058710b"
integrity sha512-mqn0kFRl0EoqhnL0GQ0veqFHyIN1yig9RHh/InzORTUiZHFRAur+aMtRkELNwGs9aNwKS6tg/An4NYBPGwvtzQ==
process-warning@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/process-warning/-/process-warning-4.0.0.tgz#581e3a7a1fb456c5f4fd239f76bce75897682d5a"
integrity sha512-/MyYDxttz7DfGMMHiysAsFE4qF+pQYAA8ziO/3NcRVrQ5fSk+Mns4QZA/oRPFzvcqNoVJXQNWNAsdwBXLUkQKw==
process@^0.11.10:
version "0.11.10"
@ -267,9 +266,9 @@ process@^0.11.10:
integrity sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==
pump@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64"
integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==
version "3.0.2"
resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.2.tgz#836f3edd6bc2ee599256c924ffe0d88573ddcbf8"
integrity sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw==
dependencies:
end-of-stream "^1.1.0"
once "^1.3.1"
@ -301,9 +300,9 @@ safe-buffer@~5.2.0:
integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
safe-stable-stringify@^2.3.1:
version "2.4.3"
resolved "https://registry.yarnpkg.com/safe-stable-stringify/-/safe-stable-stringify-2.4.3.tgz#138c84b6f6edb3db5f8ef3ef7115b8f55ccbf886"
integrity sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g==
version "2.5.0"
resolved "https://registry.yarnpkg.com/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz#4ca2f8e385f2831c432a719b108a3bf7af42a1dd"
integrity sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==
secure-json-parse@^2.4.0:
version "2.7.0"
@ -311,9 +310,9 @@ secure-json-parse@^2.4.0:
integrity sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw==
sonic-boom@^4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/sonic-boom/-/sonic-boom-4.0.1.tgz#515b7cef2c9290cb362c4536388ddeece07aed30"
integrity sha512-hTSD/6JMLyT4r9zeof6UtuBDpjJ9sO08/nmS5djaA9eozT9oOlNdpXSnzcgj4FTqpk3nkLrs61l4gip9r1HCrQ==
version "4.2.0"
resolved "https://registry.yarnpkg.com/sonic-boom/-/sonic-boom-4.2.0.tgz#e59a525f831210fa4ef1896428338641ac1c124d"
integrity sha512-INb7TM37/mAcsGmc9hyyI6+QR3rR1zVRu36B0NeGXKnOOLiZOfER5SA+N7X7k3yUYRzLWafduTDvJAfDswwEww==
dependencies:
atomic-sleep "^1.0.0"
@ -341,10 +340,10 @@ thread-stream@^3.0.0:
dependencies:
real-require "^0.2.0"
undici-types@~5.26.4:
version "5.26.5"
resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617"
integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==
undici-types@~6.19.8:
version "6.19.8"
resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.19.8.tgz#35111c9d1437ab83a7cdc0abae2f26d88eda0a02"
integrity sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==
wrappy@1:
version "1.0.2"

View file

@ -208,11 +208,7 @@
"is-plain-object",
"parse-srcset",
"deepmerge",
"core-js/features/reflect",
"@formatjs/intl-locale/polyfill",
"@formatjs/intl-locale/should-polyfill",
"@formatjs/intl-pluralrules/polyfill-force",
"@formatjs/intl-pluralrules/should-polyfill"
"core-js/features/reflect"
],
"scripts": [],
"extractLicenses": false,

View file

@ -8,7 +8,7 @@ export class AdminConfigPage {
'basic-configuration': 'APPEARANCE',
'instance-information': 'INSTANCE'
}
await go('/admin/config/edit-custom#' + tab)
await go('/admin/settings/config/edit-custom#' + tab)
await $('h2=' + waitTitles[tab]).waitForDisplayed()
}

View file

@ -3,7 +3,7 @@ import { browserSleep, go } from '../utils'
export class AdminPluginPage {
async navigateToPluginSearch () {
await go('/admin/plugins/search')
await go('/admin/settings/plugins/search')
await $('my-plugin-search').waitForDisplayed()
}

View file

@ -3,10 +3,8 @@ import { getCheckbox } from '../utils'
export class AnonymousSettingsPage {
async openSettings () {
const link = await $$('.menu-link').filter(async i => {
return await i.getText() === 'My settings'
}).then(links => links[0])
const link = await $('my-header .settings-button')
await link.waitForClickable()
await link.click()
await $('my-user-video-settings').waitForDisplayed()

View file

@ -35,19 +35,7 @@ export class LoginPage {
await submit.click()
}
if (this.isMobileDevice) {
const menuToggle = $('.top-left-block button')
await $('h2=Our content selection').waitForDisplayed()
await menuToggle.click()
await this.ensureIsLoggedInAs(displayName)
await menuToggle.click()
} else {
await this.ensureIsLoggedInAs(displayName)
}
await this.ensureIsLoggedInAs(displayName)
}
async getLoginError (username: string, password: string) {
@ -76,7 +64,7 @@ export class LoginPage {
}
async logout () {
const loggedInDropdown = $('.logged-in-more .logged-in-info')
const loggedInDropdown = $('.logged-in-container .logged-in-info')
await loggedInDropdown.waitForClickable()
await loggedInDropdown.click()
@ -87,18 +75,16 @@ export class LoginPage {
await logout.click()
await browser.waitUntil(() => {
return $$('.login-buttons-block, my-error-page a[href="/login"]').some(e => e.isDisplayed())
return $$('my-login-link, my-error-page a[href="/login"]').some(e => e.isDisplayed())
})
}
async ensureIsLoggedInAs (displayName: string) {
await this.getLoggedInInfoElem().waitForExist()
await expect(this.getLoggedInInfoElem()).toHaveText(displayName)
await this.getLoggedInInfoElem(displayName).waitForExist()
}
private getLoggedInInfoElem () {
return $('.logged-in-display-name')
private getLoggedInInfoElem (displayName: string) {
return $('.logged-in-info').$('.display-name*=' + displayName)
}
private getSuffix () {

View file

@ -77,7 +77,7 @@ export class MyAccountPage {
async countVideos (names: string[]) {
const elements = await $$('.video').filter(async e => {
const t = await e.$('.video-miniature-name').getText()
const t = await e.$('.video-name').getText()
return names.some(n => t.includes(n))
})
@ -140,7 +140,7 @@ export class MyAccountPage {
private async getVideoElement (name: string) {
const video = async () => {
const videos = await $$('.video').filter(async e => {
const t = await e.$('.video-miniature-name').getText()
const t = await e.$('.video-name').getText()
return t.includes(name)
})

View file

@ -6,7 +6,7 @@ export class SignupPage {
return $('.create-account-button')
}
async clickOnRegisterInMenu () {
async clickOnRegisterButton () {
const button = this.getRegisterMenuButton()
await button.waitForClickable()
@ -74,7 +74,7 @@ export class SignupPage {
name: string
}
}) {
await this.clickOnRegisterInMenu()
await this.clickOnRegisterButton()
await this.validateStep()
await this.checkTerms()
await this.validateStep()

View file

@ -11,9 +11,9 @@ export class VideoListPage {
// We did not upload a file on a mobile device
if (this.isMobileDevice === true || this.isSafari === true) {
url = 'https://peertube2.cpy.re/videos/local'
url = 'https://peertube2.cpy.re/videos/browse?scope=local'
} else {
url = '/videos/recently-added'
url = '/videos/browse'
}
await go(url)
@ -24,19 +24,13 @@ export class VideoListPage {
await this.waitForList()
}
async goOnLocal () {
await $('.menu-link[href="/videos/local"]').click()
await this.waitForTitle('Local videos')
}
async goOnBrowseVideos () {
await $('.menu-link*=Home').click()
async goOnRecentlyAdded () {
await $('.menu-link[href="/videos/recently-added"]').click()
await this.waitForTitle('Recently added')
}
async goOnTrending () {
await $('.menu-link[href="/videos/trending"]').click()
await this.waitForTitle('Trending')
const browseVideos = $('a*=Browse videos')
await browseVideos.waitForClickable()
await browseVideos.click()
await this.waitForList()
}
async goOnHomepage () {
@ -59,32 +53,33 @@ export class VideoListPage {
await this.waitForList()
}
getNSFWFilter () {
return $$('.active-filter').filter(async a => {
return (await a.getText()).includes('Sensitive')
}).then(f => f[0])
async getNSFWFilter () {
const el = $('.active-filter*=Sensitive')
await el.waitForDisplayed()
return el
}
async getVideosListName () {
const elems = await $$('.videos .video-miniature .video-miniature-name')
const elems = await $$('.videos .video-miniature .video-name')
const texts = await elems.map(e => e.getText())
return texts.map(t => t.trim())
}
videoExists (name: string) {
return $('.video-miniature-name=' + name).isDisplayed()
return $('.video-name=' + name).isDisplayed()
}
async videoIsBlurred (name: string) {
const filter = await $('.video-miniature-name=' + name).getCSSProperty('filter')
const filter = await $('.video-name=' + name).getCSSProperty('filter')
return filter.value !== 'none'
}
async clickOnVideo (videoName: string) {
const video = async () => {
const videos = await $$('.videos .video-miniature .video-miniature-name').filter(async e => {
const videos = await $$('.videos .video-miniature .video-name').filter(async e => {
const t = await e.getText()
return t === videoName
@ -106,7 +101,7 @@ export class VideoListPage {
async clickOnFirstVideo () {
const video = () => $('.videos .video-miniature .video-thumbnail')
const videoName = () => $('.videos .video-miniature .video-miniature-name')
const videoName = () => $('.videos .video-miniature .video-name')
await video().waitForClickable()
@ -119,7 +114,7 @@ export class VideoListPage {
}
private waitForList () {
return $('.videos .video-miniature .video-miniature-name').waitForDisplayed()
return $('.videos .video-miniature .video-name').waitForDisplayed()
}
private waitForTitle (title: string) {

View file

@ -3,7 +3,7 @@ import { getCheckbox, selectCustomSelect } from '../utils'
export class VideoUploadPage {
async navigateTo () {
const publishButton = await $('.root-header .publish-button')
const publishButton = await $('.publish-button > a')
await publishButton.waitForClickable()
await publishButton.click()
@ -28,7 +28,7 @@ export class VideoUploadPage {
// Wait for the upload to finish
await browser.waitUntil(async () => {
const warning = await $('=Publish will be available when upload is finished').isDisplayed()
const progress = await $('.progress-bar=100%').isDisplayed()
const progress = await $('.progress-container=100%').isDisplayed()
return !warning && progress
})

View file

@ -199,7 +199,7 @@ export class VideoWatchPage {
await textarea.setValue(comment)
const confirmButton = await $('.comment-buttons .orange-button')
const confirmButton = await $('.comment-buttons .primary-button')
await confirmButton.waitForClickable()
await confirmButton.click()
@ -218,12 +218,13 @@ export class VideoWatchPage {
await textarea.waitForClickable()
await textarea.setValue(comment)
const confirmButton = await $('my-video-comment .comment-buttons .orange-button')
const confirmButton = await $('my-video-comment .comment-buttons .primary-button')
await confirmButton.waitForClickable()
await confirmButton.click()
const createdComment = await (await $('.is-child .comment-html p')).getText()
const createdComment = await $('.is-child .comment-html p')
await createdComment.waitForDisplayed()
return expect(createdComment).toBe(comment)
return expect(await createdComment.getTagName()).toBe(comment)
}
}

View file

@ -13,7 +13,7 @@ describe('Plugins', () => {
}
async function expectSubmitState ({ disabled }: { disabled: boolean }) {
const disabledSubmit = await $('my-button .disabled')
const disabledSubmit = await $('my-button [disabled]')
if (disabled) expect(await disabledSubmit.isDisplayed()).toBeTruthy()
else expect(await disabledSubmit.isDisplayed()).toBeFalsy()

View file

@ -126,7 +126,7 @@ describe('Signup', () => {
})
it('Should go on signup page', async function () {
await signupPage.clickOnRegisterInMenu()
await signupPage.clickOnRegisterButton()
})
it('Should validate the first step (about page)', async function () {
@ -179,7 +179,7 @@ describe('Signup', () => {
})
it('Should go on signup page', async function () {
await signupPage.clickOnRegisterInMenu()
await signupPage.clickOnRegisterButton()
})
it('Should validate the first step (about page)', async function () {
@ -260,7 +260,7 @@ describe('Signup', () => {
})
it('Should go on signup page', async function () {
await signupPage.clickOnRegisterInMenu()
await signupPage.clickOnRegisterButton()
})
it('Should validate the first step (about page)', async function () {
@ -328,7 +328,7 @@ describe('Signup', () => {
})
it('Should go on signup page', async function () {
await signupPage.clickOnRegisterInMenu()
await signupPage.clickOnRegisterButton()
})
it('Should validate the first step (about page)', async function () {

View file

@ -52,9 +52,7 @@ describe('Videos list', () => {
async function checkCommonVideoListPages (policy: NSFWPolicy) {
const promisesWithFilters = [
videoListPage.goOnRootAccount.bind(videoListPage),
videoListPage.goOnLocal.bind(videoListPage),
videoListPage.goOnRecentlyAdded.bind(videoListPage),
videoListPage.goOnTrending.bind(videoListPage),
videoListPage.goOnBrowseVideos.bind(videoListPage),
videoListPage.goOnRootChannel.bind(videoListPage)
]

View file

@ -10,12 +10,12 @@ function isCheckboxSelected (name: string) {
}
async function selectCustomSelect (id: string, valueLabel: string) {
const wrapper = $(`[formcontrolname=${id}] .ng-arrow-wrapper`)
const wrapper = $(`[formcontrolname=${id}] span[role=combobox]`)
await wrapper.waitForClickable()
await wrapper.click()
const option = await $$(`[formcontrolname=${id}] .ng-option`).filter(async o => {
const option = await $$(`[formcontrolname=${id}] li[role=option]`).filter(async o => {
const text = await o.getText()
return text.trimStart().startsWith(valueLabel)

View file

@ -77,7 +77,7 @@ module.exports = {
},
{
browserName: 'Safari',
browserVersion: '12.1',
browserVersion: '13',
...buildBStackDesktopOptions({ sessionName: 'Safari Desktop', resolution: '1280x1024' })
},
@ -102,10 +102,11 @@ module.exports = {
...buildBStackMobileOptions({ sessionName: 'Safari iPhone', deviceName: 'iPhone 11', osVersion: '13' })
},
{
browserName: 'Safari',
...buildBStackMobileOptions({ sessionName: 'Safari iPad', deviceName: 'iPad 7th', osVersion: '13' })
...buildBStackMobileOptions({ sessionName: 'Safari iPad', deviceName: 'iPad Pro 11 2020', osVersion: '13' })
}
],

View file

@ -1,6 +1,6 @@
{
"name": "peertube-client",
"version": "6.3.3",
"version": "7.0.0-rc.1",
"private": true,
"license": "AGPL-3.0",
"author": {
@ -53,10 +53,7 @@
"@angular/platform-browser-dynamic": "^18.0.4",
"@angular/router": "^18.0.4",
"@angular/service-worker": "^18.0.4",
"@formatjs/intl-locale": "^4.0.0",
"@formatjs/intl-pluralrules": "^5.2.2",
"@ng-bootstrap/ng-bootstrap": "^17.0.0",
"@ng-select/ng-select": "^13.3.0",
"@ngx-loading-bar/core": "^6.0.0",
"@ngx-loading-bar/http-client": "^6.0.0",
"@ngx-loading-bar/router": "^6.0.0",
@ -69,7 +66,6 @@
"@types/chart.js": "^2.9.37",
"@types/core-js": "^2.5.2",
"@types/debug": "^4.1.5",
"@types/dompurify": "^3.0.5",
"@types/jschannel": "^1.0.0",
"@types/linkifyjs": "^2.1.2",
"@types/lodash-es": "^4.17.0",
@ -92,6 +88,7 @@
"buffer": "^6.0.3",
"chart.js": "^4.3.0",
"chartjs-plugin-zoom": "~2.0.1",
"color-bits": "^1.0.4",
"core-js": "^3.22.8",
"debug": "^4.3.1",
"dompurify": "^3.1.6",

View file

@ -11,7 +11,7 @@
{{ follower.name }}
</a>
<button i18n class="peertube-button-link grey-button mt-1" *ngIf="!loadedAllFollowers && canLoadMoreFollowers()" (click)="loadAllFollowers()">Show full list</button>
<button i18n class="peertube-button-link secondary-button mt-1" *ngIf="!loadedAllFollowers && canLoadMoreFollowers()" (click)="loadAllFollowers()">Show full list</button>
</div>
<div class="col-xl-6 col-md-12">
@ -23,7 +23,7 @@
{{ following.name }}
</a>
<button i18n class="peertube-button-link grey-button mt-1" *ngIf="!loadedAllFollowings && canLoadMoreFollowings()" (click)="loadAllFollowings()">Show full list</button>
<button i18n class="peertube-button-link secondary-button mt-1" *ngIf="!loadedAllFollowings && canLoadMoreFollowings()" (click)="loadAllFollowings()">Show full list</button>
</div>
</div>

View file

@ -1,15 +1,15 @@
<div class="banner" *ngIf="instanceBannerUrl">
<img [src]="instanceBannerUrl" alt="Instance banner">
</div>
<div class="margin-content mt-4">
<div class="banner mb-4" *ngIf="instanceBannerUrl">
<img class="rounded" [src]="instanceBannerUrl" alt="Instance banner">
</div>
<div class="row ">
<div class="col-md-12 col-xl-6">
<div class="d-flex justify-content-between">
<h1 i18n class="fw-semibold fs-5">About {{ instanceName }}</h1>
<a routerLink="/about/contact" i18n *ngIf="isContactFormEnabled" class="peertube-button-link orange-button h-100 d-flex align-items-center">Contact us</a>
<a routerLink="/about/contact" i18n *ngIf="isContactFormEnabled" class="peertube-button-link primary-button h-100 d-flex align-items-center">Contact us</a>
</div>
<div class="mb-4" *ngIf="categories.length !== 0 || languages.length !== 0">

View file

@ -15,8 +15,11 @@
.middle-title {
margin-top: 0;
text-transform: uppercase;
color: pvar(--fg);
font-weight: $font-bold;
@include in-content-small-title;
@include font-size(22px);
@include margin-bottom(1.5rem);
}
@ -42,9 +45,6 @@
.middle-title,
.section-title {
display: inline-block;
}
.section-title {
color: var(--mainForegroundColor);
color: pvar(--fg-400);
}
}

View file

@ -46,19 +46,18 @@
<div *ngIf="formErrors.body" class="form-error" role="alert">{{ formErrors.body }}</div>
</div>
<div *ngIf="error" class="alert alert-danger">{{ error }}</div>
<my-alert *ngIf="error" type="danger">{{ error }}</my-alert>
<div class="form-group inputs">
<input
type="button" role="button" i18n-value value="Cancel" class="peertube-button grey-button"
type="button" role="button" i18n-value value="Cancel" class="peertube-button secondary-button"
(click)="hide()" (key.enter)="hide()"
>
<input type="submit" i18n-value value="Submit" class="peertube-button orange-button" [disabled]="!form.valid" />
<input type="submit" i18n-value value="Submit" class="peertube-button primary-button" [disabled]="!form.valid" />
</div>
</form>
<div *ngIf="!isContactFormEnabled()" class="alert alert-danger" i18n>The contact form is not enabled on this instance.</div>
<my-alert *ngIf="!isContactFormEnabled()" type="danger" i18n>The contact form is not enabled on this instance.</my-alert>
</div>
</ng-template>

View file

@ -1,5 +1,6 @@
@use '_variables' as *;
@use '_mixins' as *;
@use '_form-mixins' as *;
.modal-subtitle {
line-height: 1rem;

View file

@ -1,4 +1,6 @@
import { NgClass, NgIf } from '@angular/common'
import { Component, OnInit, ViewChild } from '@angular/core'
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
import { Router } from '@angular/router'
import { Notifier, ServerService } from '@app/core'
import {
@ -9,13 +11,12 @@ import {
} from '@app/shared/form-validators/instance-validators'
import { FormReactive } from '@app/shared/shared-forms/form-reactive'
import { FormReactiveService } from '@app/shared/shared-forms/form-reactive.service'
import { AlertComponent } from '@app/shared/shared-main/common/alert.component'
import { InstanceService } from '@app/shared/shared-main/instance/instance.service'
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap/modal/modal-ref'
import { HTMLServerConfig, HttpStatusCode } from '@peertube/peertube-models'
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
import { NgIf, NgClass } from '@angular/common'
import { GlobalIconComponent } from '../../shared/shared-icons/global-icon.component'
import { InstanceService } from '@app/shared/shared-main/instance/instance.service'
type Prefill = {
subject?: string
@ -27,7 +28,7 @@ type Prefill = {
templateUrl: './contact-admin-modal.component.html',
styleUrls: [ './contact-admin-modal.component.scss' ],
standalone: true,
imports: [ GlobalIconComponent, NgIf, FormsModule, ReactiveFormsModule, NgClass ]
imports: [ GlobalIconComponent, NgIf, FormsModule, ReactiveFormsModule, NgClass, AlertComponent ]
})
export class ContactAdminModalComponent extends FormReactive implements OnInit {
@ViewChild('modal', { static: true }) modal: NgbModal

View file

@ -1,6 +1,6 @@
import { Component, Input } from '@angular/core'
import { ServerStats } from '@peertube/peertube-models'
import { BytesPipe } from '../../shared/shared-main/angular/bytes.pipe'
import { BytesPipe } from '../../shared/shared-main/common/bytes.pipe'
import { GlobalIconComponent } from '../../shared/shared-icons/global-icon.component'
import { NgIf, DecimalPipe } from '@angular/common'

View file

@ -11,12 +11,12 @@
</p>
<p i18n>
It is free and open-source software, under <a class="link-orange" href="https://github.com/Chocobozzz/PeerTube/blob/develop/LICENSE">AGPLv3
It is free and open-source software, under <a class="link-primary" href="https://github.com/Chocobozzz/PeerTube/blob/develop/LICENSE">AGPLv3
licence</a>.
</p>
<p i18n>
For more information, please visit <a class="link-orange" target="_blank" rel="noopener noreferrer" href="https://joinpeertube.org">joinpeertube.org</a>.
For more information, please visit <a class="link-primary" target="_blank" rel="noopener noreferrer" href="https://joinpeertube.org">joinpeertube.org</a>.
</p>
</div>
@ -25,7 +25,7 @@
<div class="card">
<div class="card-body">
<div class="card-title">
<a i18n class="link-orange" target="_blank" rel="noopener noreferrer" href="https://docs.joinpeertube.org/use/setup-account">Use PeerTube documentation</a>
<a i18n class="link-primary" target="_blank" rel="noopener noreferrer" href="https://docs.joinpeertube.org/use/setup-account">Use PeerTube documentation</a>
</div>
<div i18n class="card-text">
@ -37,7 +37,7 @@
<div class="card">
<div class="card-body">
<div class="card-title">
<a i18n class="link-orange" target="_blank" rel="noopener noreferrer" href="https://docs.joinpeertube.org/use/third-party-application">PeerTube Applications</a>
<a i18n class="link-primary" target="_blank" rel="noopener noreferrer" href="https://docs.joinpeertube.org/use/third-party-application">PeerTube Applications</a>
</div>
<div i18n class="card-text">
@ -49,7 +49,7 @@
<div class="card">
<div class="card-body">
<div class="card-title">
<a i18n class="link-orange" target="_blank" rel="noopener noreferrer" href="https://docs.joinpeertube.org/contribute/getting-started">Contribute on PeerTube</a>
<a i18n class="link-primary" target="_blank" rel="noopener noreferrer" href="https://docs.joinpeertube.org/contribute/getting-started">Contribute on PeerTube</a>
</div>
<div i18n class="card-text">
@ -112,7 +112,7 @@
Web peers are not publicly accessible: because we use the websocket transport, the protocol is different from classic BitTorrent tracker.
When you are in a web browser, you send a signal containing your IP address to the tracker that will randomly choose other peers
to forward the information to.
See <a class="link-orange" href="https://github.com/yciabaud/webtorrent/blob/beps/bep_webrtc.rst">this document</a> for more information
See <a class="link-primary" href="https://github.com/yciabaud/webtorrent/blob/beps/bep_webrtc.rst">this document</a> for more information
</li>
</ul>

View file

@ -1,13 +1,7 @@
<div>
<div class="sub-menu mb-0" [ngClass]="{ 'sub-menu-fixed': !isBroadcastMessageDisplayed }">
<a myPluginSelector pluginSelectorId="about-menu-instance" i18n routerLink="instance" routerLinkActive="active" class="sub-menu-entry">Instance</a>
<a myPluginSelector pluginSelectorId="about-menu-peertube" i18n routerLink="peertube" routerLinkActive="active" class="sub-menu-entry">PeerTube</a>
<a myPluginSelector pluginSelectorId="about-menu-network" i18n routerLink="follows" routerLinkActive="active" class="sub-menu-entry">Network</a>
<div class="margin-content">
<my-horizontal-menu [menuEntries]="menuEntries"></my-horizontal-menu>
</div>
<div [ngClass]="{ 'sub-menu-offset-content': !isBroadcastMessageDisplayed }">
<router-outlet></router-outlet>
</div>
<router-outlet></router-outlet>
</div>

View file

@ -1,22 +1,30 @@
import { Component } from '@angular/core'
import { ScreenService } from '@app/core'
import { RouterLink, RouterLinkActive, RouterOutlet } from '@angular/router'
import { PluginSelectorDirective } from '../shared/shared-main/plugins/plugin-selector.directive'
import { NgClass } from '@angular/common'
import { RouterOutlet } from '@angular/router'
import { HorizontalMenuComponent, HorizontalMenuEntry } from '@app/shared/shared-main/menu/horizontal-menu.component'
@Component({
selector: 'my-about',
templateUrl: './about.component.html',
standalone: true,
imports: [ NgClass, PluginSelectorDirective, RouterLink, RouterLinkActive, RouterOutlet ]
imports: [ RouterOutlet, HorizontalMenuComponent ]
})
export class AboutComponent {
constructor (
private screenService: ScreenService
) { }
get isBroadcastMessageDisplayed () {
return this.screenService.isBroadcastMessageDisplayed
}
menuEntries: HorizontalMenuEntry[] = [
{
label: $localize`Platform`,
routerLink: '/about/instance',
pluginSelectorId: 'about-menu-instance'
},
{
label: $localize`PeerTube`,
routerLink: '/about/peertube',
pluginSelectorId: 'about-menu-peertube'
},
{
label: $localize`Network`,
routerLink: '/about/follows',
pluginSelectorId: 'about-menu-network'
}
]
}

View file

@ -17,7 +17,7 @@
></my-actor-avatar>
<h2 class="fs-5 lh-1 fw-bold m-0">
<a [routerLink]="getVideoChannelLink(videoChannel)" i18n-title title="See this video channel">
<a class="text-decoration-none" [routerLink]="getVideoChannelLink(videoChannel)" i18n-title title="See this video channel">
{{ videoChannel.displayName }}
</a>
</h2>
@ -35,7 +35,7 @@
<my-subscribe-button [videoChannels]="[videoChannel]"></my-subscribe-button>
<a i18n class="button-show-channel peertube-button-link orange-button-inverted" [routerLink]="getVideoChannelLink(videoChannel)">Show this channel</a>
<a i18n class="button-show-channel peertube-button-link primary-button" [routerLink]="getVideoChannelLink(videoChannel)">Show this channel</a>
<div class="videos-overflow-workaround">
<div class="videos">
@ -47,7 +47,7 @@
></my-video-miniature>
<div *ngIf="getTotalVideosOf(videoChannel)" class="miniature-show-channel">
<a i18n [routerLink]="getVideoChannelLink(videoChannel)">SHOW THIS CHANNEL ></a>
<a class="link-primary" i18n [routerLink]="getVideoChannelLink(videoChannel)">SHOW THIS CHANNEL ></a>
</div>
</div>
</div>

View file

@ -9,7 +9,7 @@
.channel {
max-width: $max-channels-width;
background-color: pvar(--channelBackgroundColor);
background-color: pvar(--bg-secondary-350);
display: grid;
grid-template-columns: 1fr auto;
@ -36,7 +36,7 @@
}
a {
color: pvar(--mainForegroundColor);
color: pvar(--fg);
@include peertube-word-wrap;
}
@ -60,7 +60,7 @@
max-height: 80px;
@include fade-text(50px, pvar(--channelBackgroundColor));
@include fade-text(50px, pvar(--bg-secondary-350));
}
}
@ -95,14 +95,9 @@ my-subscribe-button {
height: 100%;
position: absolute;
right: 0;
background: linear-gradient(90deg, transparent 0, pvar(--channelBackgroundColor) 45px);
background: linear-gradient(90deg, transparent 0, pvar(--bg-secondary-350) 45px);
padding: (math.div($video-thumbnail-medium-height, 2) - 10px) 15px 0 60px;
z-index: z(miniature) + 1;
a {
color: pvar(--mainColor);
font-weight: $font-semibold;
}
}
.button-show-channel {
@ -130,11 +125,6 @@ my-subscribe-button {
}
}
.show-channel a {
@include peertube-button-link;
@include orange-button-inverted;
}
.videos {
display: none;
}

View file

@ -8,12 +8,12 @@ import { MiniatureDisplayOptions, VideoMiniatureComponent } from '../../shared/s
import { SubscribeButtonComponent } from '../../shared/shared-user-subscription/subscribe-button.component'
import { RouterLink } from '@angular/router'
import { ActorAvatarComponent } from '../../shared/shared-actor-image/actor-avatar.component'
import { InfiniteScrollerDirective } from '../../shared/shared-main/angular/infinite-scroller.directive'
import { InfiniteScrollerDirective } from '../../shared/shared-main/common/infinite-scroller.directive'
import { NgIf, NgFor } from '@angular/common'
import { AccountService } from '@app/shared/shared-main/account/account.service'
import { VideoChannelService } from '@app/shared/shared-main/video-channel/video-channel.service'
import { VideoChannelService } from '@app/shared/shared-main/channel/video-channel.service'
import { VideoService } from '@app/shared/shared-main/video/video.service'
import { VideoChannel } from '@app/shared/shared-main/video-channel/video-channel.model'
import { VideoChannel } from '@app/shared/shared-main/channel/video-channel.model'
import { Account } from '@app/shared/shared-main/account/account.model'
import { Video } from '@app/shared/shared-main/video/video.model'

View file

@ -1,11 +1,10 @@
<h1 class="visually-hidden" i18n>Videos</h1>
<my-videos-list
#videosList
*ngIf="account"
[title]="title"
displayTitle="false"
[getVideosObservableFunction]="getVideosObservableFunction"
[getSyndicationItemsFunction]="getSyndicationItemsFunction"

View file

@ -21,7 +21,6 @@ export class AccountVideosComponent implements OnInit, OnDestroy, DisableForReus
getVideosObservableFunction = this.getVideosObservable.bind(this)
getSyndicationItemsFunction = this.getSyndicationItems.bind(this)
title = $localize`Videos`
defaultSort = '-publishedAt' as VideoSortField
account: Account

View file

@ -1,5 +1,5 @@
<div *ngIf="account" class="root">
<div class="account-info d-md-grid d-block">
<div class="margin-content account-info d-md-grid d-block">
<div class="account-avatar-row">
<my-actor-avatar [size]="getAccountAvatarSize()" actorType="account" [actor]="account"></my-actor-avatar>
@ -48,7 +48,7 @@
<div class="description-html" [innerHTML]="accountDescriptionHTML"></div>
</div>
<button *ngIf="hasShowMoreDescription()" class="show-more d-md-none d-block button-unstyle"
<button *ngIf="hasShowMoreDescription()" class="show-more peertube-button-like-link d-md-none d-block"
(click)="accountDescriptionExpanded = !accountDescriptionExpanded"
title="Show the complete description" i18n-title i18n
>
@ -56,7 +56,7 @@
</button>
<div class="buttons">
<a *ngIf="isManageable()" routerLink="/my-account" class="peertube-button-link orange-button" i18n>
<a *ngIf="isManageable()" routerLink="/my-account" class="peertube-button-link primary-button" i18n>
Manage account
</a>
@ -64,14 +64,15 @@
</div>
</div>
<div class="links" [ngClass]="{ 'on-channel-page': isOnChannelPage() }">
<div class="margin-content horizontal-menu mb-3">
<ng-template #linkTemplate let-item="item">
<a [routerLink]="item.routerLink" routerLinkActive="active" class="sub-menu-entry">{{ item.label }}</a>
</ng-template>
<my-list-overflow [hidden]="hideMenu" [items]="links" [itemTemplate]="linkTemplate"></my-list-overflow>
<my-horizontal-menu [hidden]="hideMenu" [menuEntries]="links"></my-horizontal-menu>
<my-simple-search-input
class="ms-auto"
[alwaysShow]="!isInSmallView()" (searchChanged)="searchChanged($event)"
(inputDisplayChanged)="onSearchInputDisplayChanged($event)" name="search-videos"
i18n-iconTitle icon-title="Search account videos"

View file

@ -4,28 +4,21 @@
@use '_miniature' as *;
.root {
--myFontSize: 1rem;
--myGreyFontSize: 1rem;
--co-font-size: 1rem;
--co-secondary-font-size: 1rem;
}
.section-label {
@include section-label-responsive;
my-horizontal-menu {
flex-grow: 1;
@include margin-right(3rem);
}
.links {
.horizontal-menu {
display: flex;
justify-content: space-between;
align-items: center;
@include grid-videos-miniature-margins;
&.on-channel-page {
max-width: $max-channels-width;
}
simple-search-input {
@include margin-left(auto);
}
flex-wrap: wrap;
}
my-copy-button {
@ -36,17 +29,12 @@ my-copy-button {
grid-template-columns: 1fr min-content;
grid-template-rows: auto auto;
background-color: pvar(--submenuBackgroundColor);
@include grid-videos-miniature-margins(false, 15px);
@include padding-top(3.75rem);
@include padding-bottom(3.75rem);
@include margin-bottom(3rem);
@include font-size(1rem);
}
.account-avatar-row {
@include avatar-row-responsive(2rem, var(--myGreyFontSize));
@include avatar-row-responsive(2rem, var(--co-secondary-font-size));
}
.actor-display-name {
@ -93,7 +81,7 @@ my-copy-button {
.description:not(.expanded) {
max-height: 70px;
@include fade-text(30px, pvar(--submenuBackgroundColor));
@include fade-text(30px, pvar(--bg));
}
.buttons {
@ -103,8 +91,8 @@ my-copy-button {
@media screen and (max-width: $mobile-view) {
.root {
--myFontSize: 14px;
--myGreyFontSize: 13px;
--co-font-size: 14px;
--co-secondary-font-size: 13px;
}
.links {

View file

@ -5,8 +5,9 @@ import { AuthService, MarkdownService, MetaService, Notifier, RedirectService, R
import { Account } from '@app/shared/shared-main/account/account.model'
import { AccountService } from '@app/shared/shared-main/account/account.service'
import { DropdownAction } from '@app/shared/shared-main/buttons/action-dropdown.component'
import { VideoChannel } from '@app/shared/shared-main/video-channel/video-channel.model'
import { VideoChannelService } from '@app/shared/shared-main/video-channel/video-channel.service'
import { VideoChannel } from '@app/shared/shared-main/channel/video-channel.model'
import { VideoChannelService } from '@app/shared/shared-main/channel/video-channel.service'
import { HorizontalMenuComponent, HorizontalMenuEntry } from '@app/shared/shared-main/menu/horizontal-menu.component'
import { VideoService } from '@app/shared/shared-main/video/video.service'
import { BlocklistService } from '@app/shared/shared-moderation/blocklist.service'
import { AccountReportComponent } from '@app/shared/shared-moderation/report-modals'
@ -16,8 +17,7 @@ import { Subscription } from 'rxjs'
import { catchError, distinctUntilChanged, map, switchMap, tap } from 'rxjs/operators'
import { ActorAvatarComponent } from '../shared/shared-actor-image/actor-avatar.component'
import { CopyButtonComponent } from '../shared/shared-main/buttons/copy-button.component'
import { ListOverflowComponent, ListOverflowItem } from '../shared/shared-main/misc/list-overflow.component'
import { SimpleSearchInputComponent } from '../shared/shared-main/misc/simple-search-input.component'
import { SimpleSearchInputComponent } from '../shared/shared-main/search/simple-search-input.component'
import { AccountBlockBadgesComponent } from '../shared/shared-moderation/account-block-badges.component'
import { UserModerationDropdownComponent } from '../shared/shared-moderation/user-moderation-dropdown.component'
import { SubscribeButtonComponent } from '../shared/shared-user-subscription/subscribe-button.component'
@ -37,11 +37,12 @@ import { SubscribeButtonComponent } from '../shared/shared-user-subscription/sub
RouterLink,
SubscribeButtonComponent,
RouterLinkActive,
ListOverflowComponent,
HorizontalMenuComponent,
SimpleSearchInputComponent,
RouterOutlet,
AccountReportComponent,
DatePipe
DatePipe,
HorizontalMenuComponent
]
})
export class AccountsComponent implements OnInit, OnDestroy {
@ -52,7 +53,7 @@ export class AccountsComponent implements OnInit, OnDestroy {
videoChannels: VideoChannel[] = []
links: ListOverflowItem[] = []
links: HorizontalMenuEntry[] = []
hideMenu = false
accountVideosCount: number
@ -103,8 +104,8 @@ export class AccountsComponent implements OnInit, OnDestroy {
})
this.links = [
{ label: $localize`CHANNELS`, routerLink: 'video-channels' },
{ label: $localize`VIDEOS`, routerLink: 'videos' }
{ label: $localize`Channels`, routerLink: 'video-channels' },
{ label: $localize`Videos`, routerLink: 'videos' }
]
}

View file

@ -0,0 +1,11 @@
<div class="root">
<a i18n class="visually-hidden-focusable skip-to-content-sub-menu" href="#admin-moderation-content" (click)="$event.preventDefault(); adminContent.focus()">Skip the sub menu</a>
<div class="margin-content">
<my-horizontal-menu i18n-h1 h1="Moderation" h1Icon="moderation" [menuEntries]="menuEntries"></my-horizontal-menu>
</div>
<div #adminContent tabindex="-1" id="admin-moderation-content" class="margin-content outline-0">
<router-outlet></router-outlet>
</div>
</div>

View file

@ -0,0 +1,87 @@
import { Component, OnInit } from '@angular/core'
import { RouterOutlet } from '@angular/router'
import { AuthService, ServerService } from '@app/core'
import { HorizontalMenuComponent, HorizontalMenuEntry } from '@app/shared/shared-main/menu/horizontal-menu.component'
import { UserRight, UserRightType } from '@peertube/peertube-models'
@Component({
templateUrl: './admin-moderation.component.html',
standalone: true,
imports: [ HorizontalMenuComponent, RouterOutlet ]
})
export class AdminModerationComponent implements OnInit {
menuEntries: HorizontalMenuEntry[] = []
constructor (
private auth: AuthService,
private server: ServerService
) { }
ngOnInit () {
this.server.configReloaded.subscribe(() => this.buildMenu())
this.buildMenu()
}
private buildMenu () {
this.menuEntries = []
if (this.hasRight(UserRight.MANAGE_ABUSES)) {
this.menuEntries.push({
label: $localize`Reports`,
routerLink: '/admin/moderation/abuses/list'
})
}
if (this.hasRight(UserRight.MANAGE_REGISTRATIONS)) {
this.menuEntries.push({
label: $localize`Registrations`,
routerLink: '/admin/moderation/registrations/list'
})
}
if (this.hasRight(UserRight.MANAGE_VIDEO_BLACKLIST)) {
this.menuEntries.push({
label: $localize`Video blocks`,
routerLink: '/admin/moderation/video-blocks/list'
})
}
if (this.hasRight(UserRight.MANAGE_ACCOUNTS_BLOCKLIST) || this.hasRight(UserRight.MANAGE_SERVERS_BLOCKLIST)) {
const item: HorizontalMenuEntry = {
label: $localize`Mutes`,
routerLink: '',
children: []
}
if (this.hasRight(UserRight.MANAGE_ACCOUNTS_BLOCKLIST)) {
item.children.push({
label: $localize`Muted accounts`,
routerLink: '/admin/moderation/blocklist/accounts'
})
}
if (this.hasRight(UserRight.MANAGE_SERVERS_BLOCKLIST)) {
item.children.push({
label: $localize`Muted servers`,
routerLink: '/admin/moderation/blocklist/servers'
})
}
item.routerLink = item.children[0].routerLink
this.menuEntries.push(item)
}
if (this.hasRight(UserRight.MANAGE_INSTANCE_WATCHED_WORDS)) {
this.menuEntries.push({
label: $localize`Watched words`,
routerLink: '/admin/moderation/watched-words/list'
})
}
}
private hasRight (right: UserRightType) {
return this.auth.getUser().hasRight(right)
}
}

View file

@ -0,0 +1,11 @@
<div class="root">
<a i18n class="visually-hidden-focusable skip-to-content-sub-menu" href="#admin-overview-content" (click)="$event.preventDefault(); adminContent.focus()">Skip the sub menu</a>
<div class="margin-content">
<my-horizontal-menu i18n-h1 h1="Overview" h1Icon="overview" [menuEntries]="menuEntries"></my-horizontal-menu>
</div>
<div #adminContent tabindex="-1" id="admin-overview-content" class="margin-content outline-0">
<router-outlet></router-outlet>
</div>
</div>

View file

@ -0,0 +1,54 @@
import { Component, OnInit } from '@angular/core'
import { RouterOutlet } from '@angular/router'
import { AuthService } from '@app/core'
import { HorizontalMenuComponent, HorizontalMenuEntry } from '@app/shared/shared-main/menu/horizontal-menu.component'
import { UserRight, UserRightType } from '@peertube/peertube-models'
@Component({
templateUrl: './admin-overview.component.html',
standalone: true,
imports: [ HorizontalMenuComponent, RouterOutlet ]
})
export class AdminOverviewComponent implements OnInit {
menuEntries: HorizontalMenuEntry[] = []
constructor (
private auth: AuthService
) { }
ngOnInit () {
this.buildMenu()
}
private buildMenu () {
this.menuEntries = []
if (this.hasRight(UserRight.MANAGE_USERS)) {
this.menuEntries.push({
label: $localize`Users`,
routerLink: '/admin/overview/users'
})
}
if (this.hasRight(UserRight.SEE_ALL_VIDEOS)) {
this.menuEntries.push({
label: $localize`Videos`,
routerLink: '/admin/overview/videos',
queryParams: {
search: 'isLocal:true'
}
})
}
if (this.hasRight(UserRight.SEE_ALL_COMMENTS)) {
this.menuEntries.push({
label: $localize`Comments`,
routerLink: '/admin/overview/comments'
})
}
}
private hasRight (right: UserRightType) {
return this.auth.getUser().hasRight(right)
}
}

View file

@ -0,0 +1,11 @@
<div class="root">
<a i18n class="visually-hidden-focusable skip-to-content-sub-menu" href="#admin-settings-content" (click)="$event.preventDefault(); adminContent.focus()">Skip the sub menu</a>
<div class="margin-content">
<my-horizontal-menu i18n-h1 h1="Settings" h1Icon="config" [menuEntries]="menuEntries"></my-horizontal-menu>
</div>
<div #adminContent tabindex="-1" id="admin-settings-content" class="margin-content outline-0">
<router-outlet></router-outlet>
</div>
</div>

View file

@ -0,0 +1,179 @@
import { Component, OnInit } from '@angular/core'
import { RouterOutlet } from '@angular/router'
import { AuthService, ServerService } from '@app/core'
import { HorizontalMenuComponent, HorizontalMenuEntry } from '@app/shared/shared-main/menu/horizontal-menu.component'
import { PluginType, UserRight, UserRightType } from '@peertube/peertube-models'
@Component({
templateUrl: './admin-settings.component.html',
standalone: true,
imports: [ HorizontalMenuComponent, RouterOutlet ]
})
export class AdminSettingsComponent implements OnInit {
menuEntries: HorizontalMenuEntry[] = []
constructor (
private auth: AuthService,
private server: ServerService
) { }
ngOnInit () {
this.server.configReloaded.subscribe(() => this.buildMenu())
this.buildMenu()
}
private buildMenu () {
this.menuEntries = []
this.buildConfigurationItems()
this.buildFederationItems()
this.buildPluginItems()
this.buildRunnerItems()
this.buildSystemItems()
}
private buildFederationItems () {
if (!this.hasRight(UserRight.MANAGE_SERVER_FOLLOW)) return
this.menuEntries.push({
label: $localize`Federation`,
routerLink: '/admin/settings/follows/following-list',
children: [
{
label: $localize`Following`,
routerLink: '/admin/settings/follows/following-list'
},
{
label: $localize`Followers`,
routerLink: '/admin/settings/follows/followers-list'
},
{
label: $localize`Video redundancies`,
routerLink: '/admin/settings/follows/video-redundancies-list'
}
]
})
}
private buildConfigurationItems () {
if (this.hasRight(UserRight.MANAGE_CONFIGURATION)) {
this.menuEntries.push({ label: $localize`Configuration`, routerLink: '/admin/settings/config' })
}
}
private buildPluginItems () {
if (this.hasRight(UserRight.MANAGE_PLUGINS)) {
this.menuEntries.push({
label: $localize`Plugins/Themes`,
routerLink: '/admin/settings/plugins',
queryParams: {
pluginType: PluginType.PLUGIN
},
children: [
{
label: 'Installed plugins',
routerLink: '/admin/settings/plugins/list-installed',
queryParams: {
pluginType: PluginType.PLUGIN
}
},
{
label: 'Search plugins',
routerLink: '/admin/settings/plugins/search',
queryParams: {
pluginType: PluginType.PLUGIN
}
},
{
label: 'Installed themes',
routerLink: '/admin/settings/plugins/list-installed',
queryParams: {
pluginType: PluginType.THEME
}
},
{
label: 'Search themes',
routerLink: '/admin/settings/plugins/search',
queryParams: {
pluginType: PluginType.THEME
}
}
]
})
}
}
private buildRunnerItems () {
if (!this.isRemoteRunnersEnabled() || !this.hasRight(UserRight.MANAGE_RUNNERS)) return
this.menuEntries.push({
label: $localize`Runners`,
routerLink: '/admin/settings/system/runners/runners-list',
children: [
{
label: $localize`Remote runners`,
routerLink: '/admin/settings/system/runners/runners-list'
},
{
label: $localize`Runner jobs`,
routerLink: '/admin/settings/system/runners/jobs-list'
},
{
label: $localize`Registration tokens`,
routerLink: '/admin/settings/system/runners/registration-tokens-list'
}
]
})
}
private buildSystemItems () {
const systemItems: HorizontalMenuEntry = {
label: $localize`System`,
routerLink: '',
children: []
}
if (this.hasRight(UserRight.MANAGE_JOBS)) {
systemItems.children.push({
label: $localize`Local jobs`,
routerLink: '/admin/settings/system/jobs'
})
}
if (this.hasRight(UserRight.MANAGE_LOGS)) {
systemItems.children.push({
label: $localize`Logs`,
routerLink: '/admin/settings/system/logs'
})
}
if (this.hasRight(UserRight.MANAGE_DEBUG)) {
systemItems.children.push({
label: $localize`Debug`,
routerLink: '/admin/settings/system/debug'
})
}
if (systemItems.children.length === 0) return
systemItems.routerLink = systemItems.children[0].routerLink
this.menuEntries.push(systemItems)
}
private hasRight (right: UserRightType) {
return this.auth.getUser().hasRight(right)
}
private isRemoteRunnersEnabled () {
const config = this.server.getHTMLConfig()
return config.transcoding.remoteRunners.enabled ||
config.live.transcoding.remoteRunners.enabled ||
config.videoStudio.remoteRunners.enabled ||
config.videoTranscription.remoteRunners.enabled
}
}

View file

@ -1,7 +0,0 @@
<div class="root">
<my-top-menu-dropdown [menuEntries]="menuEntries"></my-top-menu-dropdown>
<div class="margin-content" [ngClass]="{ 'sub-menu-offset-content': !isBroadcastMessageDisplayed }">
<router-outlet></router-outlet>
</div>
</div>

View file

@ -1,10 +0,0 @@
@use '_variables' as *;
@use '_mixins' as *;
my-top-menu-dropdown {
flex-grow: 1;
}
.root {
@include sub-menu-h1;
}

View file

@ -1,240 +0,0 @@
import { NgClass } from '@angular/common'
import { Component, OnInit } from '@angular/core'
import { RouterOutlet } from '@angular/router'
import { AuthService, ScreenService, ServerService } from '@app/core'
import { ListOverflowItem } from '@app/shared/shared-main/misc/list-overflow.component'
import { TopMenuDropdownParam } from '@app/shared/shared-main/misc/top-menu-dropdown.component'
import { UserRight, UserRightType } from '@peertube/peertube-models'
import { TopMenuDropdownComponent } from '../shared/shared-main/misc/top-menu-dropdown.component'
@Component({
templateUrl: './admin.component.html',
styleUrls: [ './admin.component.scss' ],
standalone: true,
imports: [ TopMenuDropdownComponent, NgClass, RouterOutlet ]
})
export class AdminComponent implements OnInit {
items: ListOverflowItem[] = []
menuEntries: TopMenuDropdownParam[] = []
constructor (
private auth: AuthService,
private screen: ScreenService,
private server: ServerService
) { }
get isBroadcastMessageDisplayed () {
return this.screen.isBroadcastMessageDisplayed
}
ngOnInit () {
this.server.configReloaded.subscribe(() => this.buildMenu())
this.buildMenu()
}
private buildMenu () {
this.menuEntries = []
this.buildOverviewItems()
this.buildFederationItems()
this.buildModerationItems()
this.buildConfigurationItems()
this.buildPluginItems()
this.buildSystemItems()
}
private buildOverviewItems () {
const overviewItems: TopMenuDropdownParam = {
label: $localize`Overview`,
children: []
}
if (this.hasRight(UserRight.MANAGE_USERS)) {
overviewItems.children.push({
label: $localize`Users`,
routerLink: '/admin/users',
iconName: 'user'
})
}
if (this.hasRight(UserRight.SEE_ALL_VIDEOS)) {
overviewItems.children.push({
label: $localize`Videos`,
routerLink: '/admin/videos',
queryParams: {
search: 'isLocal:true'
},
iconName: 'videos'
})
}
if (this.hasRight(UserRight.SEE_ALL_COMMENTS)) {
overviewItems.children.push({
label: $localize`Comments`,
routerLink: '/admin/comments',
iconName: 'message-circle'
})
}
if (overviewItems.children.length !== 0) {
this.menuEntries.push(overviewItems)
}
}
private buildFederationItems () {
if (!this.hasRight(UserRight.MANAGE_SERVER_FOLLOW)) return
this.menuEntries.push({
label: $localize`Federation`,
children: [
{
label: $localize`Following`,
routerLink: '/admin/follows/following-list',
iconName: 'following'
},
{
label: $localize`Followers`,
routerLink: '/admin/follows/followers-list',
iconName: 'follower'
},
{
label: $localize`Video redundancies`,
routerLink: '/admin/follows/video-redundancies-list',
iconName: 'videos'
}
]
})
}
private buildModerationItems () {
const moderationItems: TopMenuDropdownParam = {
label: $localize`Moderation`,
children: []
}
if (this.hasRight(UserRight.MANAGE_REGISTRATIONS)) {
moderationItems.children.push({
label: $localize`Registrations`,
routerLink: '/admin/moderation/registrations/list',
iconName: 'user'
})
}
if (this.hasRight(UserRight.MANAGE_ABUSES)) {
moderationItems.children.push({
label: $localize`Reports`,
routerLink: '/admin/moderation/abuses/list',
iconName: 'flag'
})
}
if (this.hasRight(UserRight.MANAGE_VIDEO_BLACKLIST)) {
moderationItems.children.push({
label: $localize`Video blocks`,
routerLink: '/admin/moderation/video-blocks/list',
iconName: 'cross'
})
}
if (this.hasRight(UserRight.MANAGE_ACCOUNTS_BLOCKLIST)) {
moderationItems.children.push({
label: $localize`Muted accounts`,
routerLink: '/admin/moderation/blocklist/accounts',
iconName: 'user-x'
})
}
if (this.hasRight(UserRight.MANAGE_SERVERS_BLOCKLIST)) {
moderationItems.children.push({
label: $localize`Muted servers`,
routerLink: '/admin/moderation/blocklist/servers',
iconName: 'peertube-x'
})
}
if (this.hasRight(UserRight.MANAGE_INSTANCE_WATCHED_WORDS)) {
moderationItems.children.push({
label: $localize`Watched words`,
routerLink: '/admin/moderation/watched-words/list',
iconName: 'eye-open'
})
}
if (moderationItems.children.length !== 0) this.menuEntries.push(moderationItems)
}
private buildConfigurationItems () {
if (this.hasRight(UserRight.MANAGE_CONFIGURATION)) {
this.menuEntries.push({ label: $localize`Configuration`, routerLink: '/admin/config' })
}
}
private buildPluginItems () {
if (this.hasRight(UserRight.MANAGE_PLUGINS)) {
this.menuEntries.push({ label: $localize`Plugins/Themes`, routerLink: '/admin/plugins' })
}
}
private buildSystemItems () {
const systemItems: TopMenuDropdownParam = {
label: $localize`System`,
children: []
}
if (this.isRemoteRunnersEnabled() && this.hasRight(UserRight.MANAGE_RUNNERS)) {
systemItems.children.push({
label: $localize`Remote runners`,
iconName: 'codesandbox',
routerLink: '/admin/system/runners/runners-list'
})
systemItems.children.push({
label: $localize`Runner jobs`,
iconName: 'globe',
routerLink: '/admin/system/runners/jobs-list'
})
}
if (this.hasRight(UserRight.MANAGE_JOBS)) {
systemItems.children.push({
label: $localize`Local jobs`,
iconName: 'circle-tick',
routerLink: '/admin/system/jobs'
})
}
if (this.hasRight(UserRight.MANAGE_LOGS)) {
systemItems.children.push({
label: $localize`Logs`,
iconName: 'playlists',
routerLink: '/admin/system/logs'
})
}
if (this.hasRight(UserRight.MANAGE_DEBUG)) {
systemItems.children.push({
label: $localize`Debug`,
iconName: 'cog',
routerLink: '/admin/system/debug'
})
}
if (systemItems.children.length !== 0) {
this.menuEntries.push(systemItems)
}
}
private hasRight (right: UserRightType) {
return this.auth.getUser().hasRight(right)
}
private isRemoteRunnersEnabled () {
const config = this.server.getHTMLConfig()
return config.transcoding.remoteRunners.enabled ||
config.live.transcoding.remoteRunners.enabled ||
config.videoStudio.remoteRunners.enabled ||
config.videoTranscription.remoteRunners.enabled
}
}

View file

@ -3,7 +3,7 @@ import { EditCustomConfigComponent } from '@app/+admin/config/edit-custom-config
import { UserRightGuard } from '@app/core'
import { UserRight } from '@peertube/peertube-models'
export const ConfigRoutes: Routes = [
export const configRoutes: Routes = [
{
path: 'config',
canActivate: [ UserRightGuard ],

View file

@ -1,7 +1,7 @@
import { Component, Input } from '@angular/core'
import { FormGroup, FormsModule, ReactiveFormsModule } from '@angular/forms'
import { PeerTubeTemplateDirective } from '../../../shared/shared-main/angular/peertube-template.directive'
import { HelpComponent } from '../../../shared/shared-main/misc/help.component'
import { PeerTubeTemplateDirective } from '../../../shared/shared-main/common/peertube-template.directive'
import { HelpComponent } from '../../../shared/shared-main/buttons/help.component'
import { NgClass, NgIf } from '@angular/common'
@Component({

View file

@ -4,7 +4,7 @@
<h2 i18n>APPEARANCE</h2>
<div i18n class="inner-form-description">
Use <a class="link-orange" routerLink="/admin/plugins">plugins & themes</a> for more involved changes, or add slight <a class="link-orange" routerLink="/admin/config/edit-custom" fragment="advanced-configuration">customizations</a>.
Use <a class="link-primary" routerLink="/admin/settings/plugins">plugins & themes</a> for more involved changes, or add slight <a class="link-primary" routerLink="/admin/settings/config/edit-custom" fragment="advanced-configuration">customizations</a>.
</div>
</div>
@ -14,21 +14,16 @@
<div class="form-group">
<label i18n for="themeDefault">Theme</label>
<div class="peertube-select-container">
<select formControlName="default" id="themeDefault" class="form-control">
<option i18n value="default">{{ getDefaultThemeLabel() }}</option>
<option *ngFor="let theme of availableThemes" [value]="theme.id">{{ theme.label }}</option>
</select>
</div>
<my-select-options formControlName="default" inputId="themeDefault" [items]="availableThemes"></my-select-options>
</div>
</ng-container>
<div class="form-group" formGroupName="instance">
<label i18n for="instanceDefaultClientRoute">Landing page</label>
<label i18n id="instanceDefaultClientRouteLabel" for="instanceDefaultClientRoute">Landing page</label>
<my-select-custom-value
id="instanceDefaultClientRoute"
labelId="instanceDefaultClientRouteLabel"
inputId="instanceDefaultClientRoute"
[items]="defaultLandingPageOptions"
formControlName="defaultClientRoute"
inputType="text"
@ -41,7 +36,7 @@
<div class="form-group" formGroupName="trending">
<ng-container formGroupName="videos">
<ng-container formGroupName="algorithms">
<label i18n for="trendingVideosAlgorithmsDefault">Default trending page</label>
<label i18n for="trendingVideosAlgorithmsDefault">Default trending algorithm</label>
<div class="peertube-select-container">
<select id="trendingVideosAlgorithmsDefault" formControlName="default" class="form-control">
@ -134,7 +129,7 @@
<label i18n for="broadcastMessageMessage">Message</label><my-help helpType="markdownText"></my-help>
<my-markdown-textarea
name="broadcastMessageMessage" formControlName="message"
inputId="broadcastMessageMessage" formControlName="message"
[formError]="formErrors['broadcastMessage.message']" markdownType="to-unsafe-html"
></my-markdown-textarea>
@ -150,7 +145,7 @@
<div class="title-col">
<h2 i18n>NEW USERS</h2>
<div i18n class="inner-form-description">
Manage <a class="link-orange" routerLink="/admin/users">users</a> to set their quota individually.
Manage <a class="link-primary" routerLink="/admin/overview/users">users</a> to set their quota individually.
</div>
</div>
@ -165,7 +160,7 @@
<ng-container ngProjectAs="description">
<span i18n>⚠️ This functionality requires a lot of attention and extra moderation.</span>
<div class="alert pt-alert-primary alert-signup" *ngIf="signupAlertMessage">{{ signupAlertMessage }}</div>
<my-alert type="primary" class="alert-signup" *ngIf="signupAlertMessage">{{ signupAlertMessage }}</my-alert>
</ng-container>
<ng-container ngProjectAs="extra">
@ -185,6 +180,7 @@
<div [ngClass]="getDisabledSignupClass()">
<label i18n for="signupLimit">Signup limit</label>
<span i18n class="small muted ms-1">When the total number of users in your instance reaches this limit, registrations are disabled. -1 == unlimited</span>
<div class="number-with-unit">
<input
@ -203,11 +199,11 @@
<label i18n for="signupMinimumAge">Minimum required age to create an account</label>
<div class="number-with-unit">
<input
type="number" min="1" id="signupMinimumAge" class="form-control"
formControlName="minimumAge" [ngClass]="{ 'input-error': formErrors['signup.minimumAge'] }"
>
<span i18n>{form.value['signup']['minimumAge'], plural, =1 {year old} other {years old}}</span>
<input
type="number" min="1" id="signupMinimumAge" class="form-control"
formControlName="minimumAge" [ngClass]="{ 'input-error': formErrors['signup.minimumAge'] }"
>
<span i18n>{form.value['signup']['minimumAge'], plural, =1 {year old} other {years old}}</span>
</div>
<div *ngIf="formErrors.signup.minimumAge" class="form-error" role="alert">{{ formErrors.signup.minimumAge }}</div>
@ -219,10 +215,11 @@
<ng-container formGroupName="user">
<div class="form-group">
<label i18n for="userVideoQuota">Default video quota per user</label>
<label i18n id="userVideoQuotaLabel" for="userVideoQuota">Default video quota per user</label>
<my-select-custom-value
id="userVideoQuota"
labelId="userVideoQuotaLabel"
inputId="userVideoQuota"
[items]="getVideoQuotaOptions()"
formControlName="videoQuota"
i18n-inputSuffix inputSuffix="bytes" inputType="number"
@ -235,10 +232,11 @@
</div>
<div class="form-group">
<label i18n for="userVideoQuotaDaily">Default daily upload limit per user</label>
<label i18n id="userVideoQuotaDaily" for="userVideoQuotaDaily">Default daily upload limit per user</label>
<my-select-custom-value
id="userVideoQuotaDaily"
labelId="userVideoQuotaDailyLabel"
inputId="userVideoQuotaDaily"
[items]="getVideoQuotaDailyOptions()"
formControlName="videoQuotaDaily"
i18n-inputSuffix inputSuffix="bytes" inputType="number"
@ -279,7 +277,7 @@
<span i18n class="small muted ms-1">allows to import multiple videos in parallel. ⚠️ Requires a PeerTube restart.</span>
<div class="number-with-unit">
<input type="number" name="importConcurrency" formControlName="concurrency" />
<input type="number" id="importConcurrency" formControlName="concurrency" />
<span i18n>jobs in parallel</span>
</div>
@ -292,7 +290,7 @@
i18n-labelText labelText="Allow import with HTTP URL (e.g. YouTube)"
>
<ng-container ngProjectAs="description">
<span i18n>⚠️ If enabled, we recommend to use <a class="link-orange" href="https://docs.joinpeertube.org/maintain/configuration#security">a HTTP proxy</a> to prevent private URL access from your PeerTube server</span>
<span i18n>⚠️ If enabled, we recommend to use <a class="link-primary" href="https://docs.joinpeertube.org/maintain/configuration#security">a HTTP proxy</a> to prevent private URL access from your PeerTube server</span>
</ng-container>
</my-peertube-checkbox>
</div>
@ -323,6 +321,20 @@
</ng-container>
</my-peertube-checkbox>
</div>
<div class="form-group">
<label i18n for="videoChannelSynchronizationMaxPerUser">Max channel synchronization per user</label>
<div class="number-with-unit">
<input
type="number" min="1" id="videoChannelSynchronizationMaxPerUser" class="form-control"
formControlName="maxPerUser" [ngClass]="{ 'input-error': formErrors['import']['videoChannelSynchronization']['maxPerUser'] }"
>
<span i18n>{form.value['import']['videoChannelSynchronization']['maxPerUser'], plural, =1 {sync} other {syncs}}</span>
</div>
<div *ngIf="formErrors.import.videoChannelSynchronization.maxPerUser" class="form-error" role="alert">{{ formErrors.import.videoChannelSynchronization.maxPerUser }}</div>
</div>
</ng-container>
</ng-container>
@ -389,7 +401,7 @@
>
<ng-container ngProjectAs="description">
<span i18n>
Use <a routerLink="/admin/system/runners/runners-list">remote runners</a> to process transcription tasks.
Use <a routerLink="/admin/settings/system/runners/runners-list">remote runners</a> to process transcription tasks.
Remote runners has to register on your instance first.
</span>
</ng-container>
@ -468,7 +480,7 @@
<div i18n>⚠️ This functionality depends heavily on the moderation of instances followed by the search index you select.</div>
<div i18n>
You should only use moderated search indexes in production, or <a class="link-orange" href="https://framagit.org/framasoft/peertube/search-index">host your own</a>.
You should only use moderated search indexes in production, or <a class="link-primary" href="https://framagit.org/framasoft/peertube/search-index">host your own</a>.
</div>
</ng-container>
@ -552,12 +564,13 @@
<ng-container ngProjectAs="extra">
<div class="form-group" [ngClass]="getDisabledExportUsersClass()">
<label i18n for="exportUsersMaxUserVideoQuota">Max user video quota allowed to generate the export</label>
<label i18n id="exportUsersMaxUserVideoQuota" for="exportUsersMaxUserVideoQuota">Max user video quota allowed to generate the export</label>
<span i18n class="ms-2 small muted">If the user decides to include the video files in the archive</span>
<my-select-custom-value
id="exportUsersMaxUserVideoQuota"
labelId="exportUsersMaxUserVideoQuota"
inputId="exportUsersMaxUserVideoQuota"
[items]="exportMaxUserVideoQuotaOptions"
formControlName="maxUserVideoQuota"
i18n-inputSuffix inputSuffix="bytes" inputType="number"
@ -570,10 +583,7 @@
<div class="form-group" [ngClass]="getDisabledExportUsersClass()">
<label i18n for="exportUsersExportExpiration">User export expiration</label>
<my-select-options
labelForId="exportUsersExportExpiration" [items]="exportExpirationOptions" formControlName="exportExpiration"
bindLabel="label" bindValue="value" [clearable]="false" [searchable]="false"
></my-select-options>
<my-select-options inputId="exportUsersExportExpiration" [items]="exportExpirationOptions" formControlName="exportExpiration"></my-select-options>
<div i18n class="mt-1 small muted">The archive file is deleted after this period.</div>
@ -593,7 +603,7 @@
<div class="title-col">
<h2 i18n>FEDERATION</h2>
<div i18n class="inner-form-description">
Manage <a class="link-orange" routerLink="/admin/follows">relations</a> with other instances.
Manage <a class="link-primary" routerLink="/admin/settings/follows">relations</a> with other instances.
</div>
</div>
@ -644,7 +654,7 @@
<div i18n>⚠️ This functionality requires a lot of attention and extra moderation.</div>
<span i18n>
See <a class="link-orange" href="https://docs.joinpeertube.org/admin/following-instances#automatically-follow-other-instances" rel="noopener noreferrer" target="_blank">the documentation</a> for more information about the expected URL
See <a class="link-primary" href="https://docs.joinpeertube.org/admin/following-instances#automatically-follow-other-instances" rel="noopener noreferrer" target="_blank">the documentation</a> for more information about the expected URL
</span>
</ng-container>

View file

@ -1,19 +1,19 @@
import { pairwise } from 'rxjs/operators'
import { SelectOptionsItem } from 'src/types/select-options-item.model'
import { NgClass, NgFor, NgIf } from '@angular/common'
import { Component, Input, OnChanges, OnInit, SimpleChanges } from '@angular/core'
import { FormGroup, FormsModule, ReactiveFormsModule } from '@angular/forms'
import { MenuService, ThemeService } from '@app/core'
import { RouterLink } from '@angular/router'
import { ThemeService } from '@app/core'
import { AlertComponent } from '@app/shared/shared-main/common/alert.component'
import { HTMLServerConfig } from '@peertube/peertube-models'
import { ConfigService } from '../shared/config.service'
import { PeerTubeTemplateDirective } from '../../../shared/shared-main/angular/peertube-template.directive'
import { SelectOptionsComponent } from '../../../shared/shared-forms/select/select-options.component'
import { UserRealQuotaInfoComponent } from '../../shared/user-real-quota-info.component'
import { pairwise } from 'rxjs/operators'
import { SelectOptionsItem } from 'src/types/select-options-item.model'
import { MarkdownTextareaComponent } from '../../../shared/shared-forms/markdown-textarea.component'
import { HelpComponent } from '../../../shared/shared-main/misc/help.component'
import { PeertubeCheckboxComponent } from '../../../shared/shared-forms/peertube-checkbox.component'
import { SelectCustomValueComponent } from '../../../shared/shared-forms/select/select-custom-value.component'
import { NgFor, NgIf, NgClass } from '@angular/common'
import { RouterLink } from '@angular/router'
import { SelectOptionsComponent } from '../../../shared/shared-forms/select/select-options.component'
import { HelpComponent } from '../../../shared/shared-main/buttons/help.component'
import { UserRealQuotaInfoComponent } from '../../shared/user-real-quota-info.component'
import { ConfigService } from '../shared/config.service'
@Component({
selector: 'my-edit-basic-configuration',
@ -33,7 +33,7 @@ import { RouterLink } from '@angular/router'
NgClass,
UserRealQuotaInfoComponent,
SelectOptionsComponent,
PeerTubeTemplateDirective
AlertComponent
]
})
export class EditBasicConfigurationComponent implements OnInit, OnChanges {
@ -51,7 +51,6 @@ export class EditBasicConfigurationComponent implements OnInit, OnChanges {
constructor (
private configService: ConfigService,
private menuService: MenuService,
private themeService: ThemeService
) {}
@ -60,7 +59,11 @@ export class EditBasicConfigurationComponent implements OnInit, OnChanges {
this.checkSignupField()
this.checkImportSyncField()
this.availableThemes = this.themeService.buildAvailableThemes()
this.availableThemes = [
this.themeService.getDefaultThemeItem(),
...this.themeService.buildAvailableThemes()
]
this.exportExpirationOptions = [
{ id: 1000 * 3600 * 24, label: $localize`1 day` },
@ -154,17 +157,22 @@ export class EditBasicConfigurationComponent implements OnInit, OnChanges {
}
buildLandingPageOptions () {
this.defaultLandingPageOptions = this.menuService.buildCommonLinks(this.serverConfig)
.links
.map(o => ({
id: o.path,
label: o.label,
description: o.path
}))
}
let links: { label: string, path: string }[] = []
getDefaultThemeLabel () {
return this.themeService.getDefaultThemeLabel()
if (this.serverConfig.homepage.enabled) {
links.push({ label: $localize`Home`, path: '/home' })
}
links = links.concat([
{ label: $localize`Discover`, path: '/videos/overview' },
{ label: $localize`Browse videos`, path: '/videos/browse' }
])
this.defaultLandingPageOptions = links.map(o => ({
id: o.path,
label: o.label,
description: o.path
}))
}
private checkImportSyncField () {

View file

@ -1,10 +1,10 @@
<h1 class="visually-hidden" i18n>Configuration</h1>
<div class="alert alert-warning" *ngIf="!isUpdateAllowed()" i18n>
<my-alert type="warning" *ngIf="!isUpdateAllowed()" i18n>
Updating instance configuration from the web interface is disabled by the system administrator.
</div>
</my-alert>
<form role="form" [formGroup]="form">
<form [formGroup]="form">
<div ngbNav #nav="ngbNav" [activeId]="activeNav" (activeIdChange)="onNavChange($event)" class="nav-tabs">
@ -87,6 +87,7 @@
</span>
<input
class="peertube-button primary-button"
(click)="formValidated()" type="submit" i18n-value value="Update configuration"
[disabled]="!form.valid || !hasConsistentOptions() || !isUpdateAllowed()"
>

View file

@ -1,5 +1,6 @@
@use '_variables' as *;
@use '_mixins' as *;
@use '_form-mixins' as *;
$form-base-input-width: 340px;
$form-max-width: 500px;
@ -33,8 +34,8 @@ input[type=number] {
input[type=number] + span {
position: absolute;
top: 0.2em;
right: 2.5rem;
top: 0.4em;
right: 3em;
@media screen and (max-width: $mobile-view) {
display: none;
@ -42,13 +43,13 @@ input[type=number] {
}
input[disabled] {
background-color: #f9f9f9;
opacity: 0.8;
pointer-events: none;
}
}
input[type=checkbox] {
@include peertube-checkbox(1px);
@include peertube-checkbox;
}
.peertube-select-container {
@ -56,8 +57,7 @@ input[type=checkbox] {
}
my-select-options,
my-select-custom-value,
my-select-checkbox {
my-select-custom-value {
display: block;
@include responsive-width($form-base-input-width);
@ -66,8 +66,6 @@ my-select-checkbox {
input[type=submit] {
display: flex;
@include peertube-button;
@include orange-button;
@include margin-left(auto);
+ .form-error {
@ -132,18 +130,6 @@ ngb-tabset:not(.previews) ::ng-deep {
height: 0;
width: 100%;
justify-content: right;
.callout-link {
position: relative;
right: 3.3em;
top: .3em;
font-size: 90%;
color: pvar(--mainColor);
background-color: pvar(--mainBackgroundColor);
padding: 0 .3em;
@include peertube-button-link;
}
}
my-actor-banner-edit {

View file

@ -16,6 +16,7 @@ import {
INSTANCE_SHORT_DESCRIPTION_VALIDATOR,
MAX_INSTANCE_LIVES_VALIDATOR,
MAX_LIVE_DURATION_VALIDATOR,
MAX_SYNC_PER_USER,
MAX_USER_LIVES_VALIDATOR,
MAX_VIDEO_CHANNELS_PER_USER_VALIDATOR,
SEARCH_INDEX_URL_VALIDATOR,
@ -28,6 +29,7 @@ import {
import { USER_VIDEO_QUOTA_DAILY_VALIDATOR, USER_VIDEO_QUOTA_VALIDATOR } from '@app/shared/form-validators/user-validators'
import { FormReactive } from '@app/shared/shared-forms/form-reactive'
import { FormReactiveService } from '@app/shared/shared-forms/form-reactive.service'
import { AlertComponent } from '@app/shared/shared-main/common/alert.component'
import { CustomPageService } from '@app/shared/shared-main/custom-page/custom-page.service'
import { NgbNav, NgbNavContent, NgbNavItem, NgbNavLink, NgbNavLinkBase, NgbNavOutlet } from '@ng-bootstrap/ng-bootstrap'
import { CustomConfig, CustomPage, HTMLServerConfig } from '@peertube/peertube-models'
@ -68,7 +70,8 @@ type ComponentCustomConfig = CustomConfig & {
EditLiveConfigurationComponent,
EditAdvancedConfigurationComponent,
NgbNavOutlet,
NgFor
NgFor,
AlertComponent
]
})
export class EditCustomConfigComponent extends FormReactive implements OnInit {
@ -180,7 +183,9 @@ export class EditCustomConfigComponent extends FormReactive implements OnInit {
}
},
videoChannelSynchronization: {
enabled: null
enabled: null,
maxPerUser: MAX_SYNC_PER_USER
},
users: {
enabled: null

View file

@ -16,7 +16,7 @@
</div>
<my-markdown-textarea
name="instanceCustomHomepageContent" formControlName="content"
inputId="instanceCustomHomepageContent" formControlName="content"
[customMarkdownRenderer]="getCustomMarkdownRenderer()" [debounceTime]="500"
[formError]="formErrors['instanceCustomHomepage.content']"
dir="ltr"

View file

@ -65,7 +65,7 @@
</div>
<my-markdown-textarea
name="instanceDescription" formControlName="description"
inputId="instanceDescription" formControlName="description"
[customMarkdownRenderer]="getCustomMarkdownRenderer()" [debounceTime]="500"
[formError]="formErrors['instance.description']"
></my-markdown-textarea>
@ -76,7 +76,7 @@
<div>
<my-select-checkbox
id="instanceCategories"
inputId="instanceCategories"
formControlName="categories" [availableItems]="categoryItems"
[selectableGroup]="false"
i18n-placeholder placeholder="Add a new category"
@ -90,7 +90,7 @@
<div>
<my-select-checkbox
id="instanceLanguages"
inputId="instanceLanguages"
formControlName="languages" [availableItems]="languageItems"
[selectableGroup]="false"
i18n-placeholder placeholder="Add a new language"
@ -106,7 +106,7 @@
<div class="title-col">
<h2 i18n>MODERATION & NSFW</h2>
<div i18n class="inner-form-description">
Manage <a class="link-orange" routerLink="/admin/users">users</a> to build a moderation team.
Manage <a class="link-primary" routerLink="/admin/overview/users">users</a> to build a moderation team.
</div>
</div>
@ -152,7 +152,7 @@
<label i18n for="instanceTerms">Terms</label><my-help helpType="markdownText"></my-help>
<my-markdown-textarea
name="instanceTerms" formControlName="terms" markdownType="enhanced"
inputId="instanceTerms" formControlName="terms" markdownType="enhanced"
[formError]="formErrors['instance.terms']"
></my-markdown-textarea>
</div>
@ -161,7 +161,7 @@
<label i18n for="instanceCodeOfConduct">Code of conduct</label><my-help helpType="markdownText"></my-help>
<my-markdown-textarea
name="instanceCodeOfConduct" formControlName="codeOfConduct" markdownType="enhanced"
inputId="instanceCodeOfConduct" formControlName="codeOfConduct" markdownType="enhanced"
[formError]="formErrors['instance.codeOfConduct']"
></my-markdown-textarea>
</div>
@ -171,7 +171,7 @@
<div i18n class="label-small-info">Who moderates the instance? What is the policy regarding NSFW videos? Political videos? etc</div>
<my-markdown-textarea
name="instanceModerationInformation" formControlName="moderationInformation" markdownType="enhanced"
inputId="instanceModerationInformation" formControlName="moderationInformation" markdownType="enhanced"
[formError]="formErrors['instance.moderationInformation']"
></my-markdown-textarea>
</div>
@ -191,7 +191,7 @@
<div i18n class="label-small-info">A single person? A non-profit? A company?</div>
<my-markdown-textarea
name="instanceAdministrator" formControlName="administrator" markdownType="enhanced"
inputId="instanceAdministrator" formControlName="administrator" markdownType="enhanced"
[formError]="formErrors['instance.administrator']"
></my-markdown-textarea>
</div>
@ -201,7 +201,7 @@
<div i18n class="label-small-info">To share your personal videos? To open registrations and allow people to upload what they want?</div>
<my-markdown-textarea
name="instanceCreationReason" formControlName="creationReason" markdownType="enhanced"
inputId="instanceCreationReason" formControlName="creationReason" markdownType="enhanced"
[formError]="formErrors['instance.creationReason']"
></my-markdown-textarea>
</div>
@ -211,7 +211,7 @@
<div i18n class="label-small-info">It's important to know for users who want to register on your instance</div>
<my-markdown-textarea
name="instanceMaintenanceLifetime" formControlName="maintenanceLifetime" markdownType="enhanced"
inputId="instanceMaintenanceLifetime" formControlName="maintenanceLifetime" markdownType="enhanced"
[formError]="formErrors['instance.maintenanceLifetime']"
></my-markdown-textarea>
</div>
@ -221,7 +221,7 @@
<div i18n class="label-small-info">With your own funds? With user donations? Advertising?</div>
<my-markdown-textarea
name="instanceBusinessModel" formControlName="businessModel" markdownType="enhanced"
inputId="instanceBusinessModel" formControlName="businessModel" markdownType="enhanced"
[formError]="formErrors['instance.businessModel']"
></my-markdown-textarea>
</div>
@ -241,7 +241,7 @@
<div i18n class="label-small-info">i.e. 2vCore 2GB RAM, a direct the link to the server you rent, etc.</div>
<my-markdown-textarea
name="instanceHardwareInformation" formControlName="hardwareInformation" markdownType="enhanced"
inputId="instanceHardwareInformation" formControlName="hardwareInformation" markdownType="enhanced"
[formError]="formErrors['instance.hardwareInformation']"
></my-markdown-textarea>
</div>

View file

@ -16,8 +16,8 @@ import { CustomMarkupHelpComponent } from '../../../shared/shared-custom-markup/
import { MarkdownTextareaComponent } from '../../../shared/shared-forms/markdown-textarea.component'
import { PeertubeCheckboxComponent } from '../../../shared/shared-forms/peertube-checkbox.component'
import { SelectCheckboxComponent } from '../../../shared/shared-forms/select/select-checkbox.component'
import { PeerTubeTemplateDirective } from '../../../shared/shared-main/angular/peertube-template.directive'
import { HelpComponent } from '../../../shared/shared-main/misc/help.component'
import { PeerTubeTemplateDirective } from '../../../shared/shared-main/common/peertube-template.directive'
import { HelpComponent } from '../../../shared/shared-main/buttons/help.component'
@Component({
selector: 'my-edit-instance-information',

View file

@ -51,7 +51,7 @@
<span i18n class="ms-2 small muted">(-1 for "unlimited")</span>
<div class="number-with-unit">
<input type="number" name="liveMaxInstanceLives" formControlName="maxInstanceLives" />
<input type="number" id="liveMaxInstanceLives" formControlName="maxInstanceLives" />
<span i18n>{form.value['live']['maxInstanceLives'], plural, =1 {live} other {lives}}</span>
</div>
@ -63,7 +63,7 @@
<span i18n class="ms-2 small muted">(-1 for "unlimited")</span>
<div class="number-with-unit">
<input type="number" name="liveMaxUserLives" formControlName="maxUserLives" />
<input type="number" id="liveMaxUserLives" formControlName="maxUserLives" />
<span i18n>{form.value['live']['maxUserLives'], plural, =1 {live} other {lives}}</span>
</div>
@ -73,10 +73,7 @@
<div class="form-group" [ngClass]="getDisabledLiveClass()">
<label i18n for="liveMaxDuration">Max live duration</label>
<my-select-options
labelForId="liveMaxDuration" [items]="liveMaxDurationOptions" formControlName="maxDuration"
bindLabel="label" bindValue="value" [clearable]="false" [searchable]="true"
></my-select-options>
<my-select-options inputId="liveMaxDuration" [items]="liveMaxDurationOptions" formControlName="maxDuration"></my-select-options>
<div *ngIf="formErrors.live.maxDuration" class="form-error" role="alert">{{ formErrors.live.maxDuration }}</div>
</div>
@ -169,7 +166,7 @@
>
<ng-container ngProjectAs="description">
<span i18n>
Use <a routerLink="/admin/system/runners/runners-list">remote runners</a> to process live transcoding.
Use <a routerLink="/admin/settings/system/runners/runners-list">remote runners</a> to process live transcoding.
Remote runners has to register on your instance first.
</span>
</ng-container>
@ -177,7 +174,7 @@
</div>
<div class="form-group" [ngClass]="getDisabledLiveLocalTranscodingClass()">
<label i18n for="liveTranscodingThreads">Live transcoding threads</label>
<label i18n id="liveTranscodingThreadsLabel" for="liveTranscodingThreads">Live transcoding threads</label>
<span class="small muted ms-1">
<ng-container *ngIf="getTotalTranscodingThreads().atMost" i18n>
@ -190,7 +187,8 @@
</span>
<my-select-custom-value
id="liveTranscodingThreads"
labelId="liveTranscodingThreadsLabel"
inputId="liveTranscodingThreads"
[items]="transcodingThreadOptions"
formControlName="threads"
[clearable]="false"
@ -202,13 +200,7 @@
<label i18n for="liveTranscodingProfile">Live transcoding profile</label>
<span class="small muted ms-1" i18n>new live transcoding profiles can be added by PeerTube plugins</span>
<my-select-options
id="liveTranscodingProfile"
formControlName="profile"
[items]="transcodingProfiles"
[clearable]="false"
>
</my-select-options>
<my-select-options inputId="liveTranscodingProfile" formControlName="profile" [items]="transcodingProfiles"></my-select-options>
<div *ngIf="formErrors.live.transcoding.profile" class="form-error" role="alert">{{ formErrors.live.transcoding.profile }}</div>
</div>

View file

@ -8,7 +8,7 @@ import { SelectCustomValueComponent } from '../../../shared/shared-forms/select/
import { RouterLink } from '@angular/router'
import { SelectOptionsComponent } from '../../../shared/shared-forms/select/select-options.component'
import { NgClass, NgIf, NgFor } from '@angular/common'
import { PeerTubeTemplateDirective } from '../../../shared/shared-main/angular/peertube-template.directive'
import { PeerTubeTemplateDirective } from '../../../shared/shared-main/common/peertube-template.directive'
import { PeertubeCheckboxComponent } from '../../../shared/shared-forms/peertube-checkbox.component'
@Component({

View file

@ -4,7 +4,7 @@
<div class="title-col"></div>
<div class="content-col">
<div class="callout callout-orange">
<div class="callout callout-primary">
<span i18n>
Estimating a server's capacity to transcode and stream videos isn't easy and we can't tune PeerTube automatically.
</span>
@ -12,7 +12,7 @@
<br />
<span i18n>
However, you may want to read <a class="link-orange" target="_blank" rel="noopener noreferrer" href="https://docs.joinpeertube.org/admin/configuration#vod-transcoding">our guidelines</a> before tweaking the following values.
However, you may want to read <a class="link-primary" target="_blank" rel="noopener noreferrer" href="https://docs.joinpeertube.org/admin/configuration#vod-transcoding">our guidelines</a> before tweaking the following values.
</span>
</div>
</div>
@ -192,7 +192,7 @@
>
<ng-container ngProjectAs="description">
<span i18n>
Use <a routerLink="/admin/system/runners/runners-list">remote runners</a> to process VOD transcoding.
Use <a routerLink="/admin/settings/system/runners/runners-list">remote runners</a> to process VOD transcoding.
Remote runners has to register on your instance first.
</span>
</ng-container>
@ -200,7 +200,7 @@
</div>
<div class="form-group mt-4" [ngClass]="getLocalTranscodingDisabledClass()">
<label i18n for="transcodingThreads">Transcoding threads</label>
<label i18n id="transcodingThreadsLabel" for="transcodingThreads">Transcoding threads</label>
<span class="small muted ms-1">
<ng-container *ngIf="getTotalTranscodingThreads().atMost" i18n>
@ -213,7 +213,8 @@
</span>
<my-select-custom-value
id="transcodingThreads"
labelId="transcodingThreadsLabel"
inputId="transcodingThreads"
[items]="transcodingThreadOptions"
formControlName="threads"
[clearable]="false"
@ -227,7 +228,7 @@
<span class="small muted ms-1" i18n>allows to transcode multiple files in parallel. ⚠️ Requires a PeerTube restart</span>
<div class="number-with-unit">
<input type="number" name="transcodingConcurrency" formControlName="concurrency" />
<input type="number" id="transcodingConcurrency" formControlName="concurrency" />
<span i18n>jobs in parallel</span>
</div>
@ -238,12 +239,7 @@
<label i18n for="transcodingProfile">Transcoding profile</label>
<span class="small muted ms-1" i18n>new transcoding profiles can be added by PeerTube plugins</span>
<my-select-options
id="transcodingProfile"
formControlName="profile"
[items]="transcodingProfiles"
[clearable]="false"
></my-select-options>
<my-select-options inputId="transcodingProfile" formControlName="profile" [items]="transcodingProfiles"></my-select-options>
<div *ngIf="formErrors.transcoding.profile" class="form-error" role="alert">{{ formErrors.transcoding.profile }}</div>
</div>
@ -282,7 +278,7 @@
>
<ng-container ngProjectAs="description">
<span i18n>
Use <a routerLink="/admin/system/runners/runners-list">remote runners</a> to process studio transcoding tasks.
Use <a routerLink="/admin/settings/system/runners/runners-list">remote runners</a> to process studio transcoding tasks.
Remote runners has to register on your instance first.
</span>
</ng-container>

View file

@ -8,7 +8,7 @@ import { SelectOptionsItem } from 'src/types/select-options-item.model'
import { PeertubeCheckboxComponent } from '../../../shared/shared-forms/peertube-checkbox.component'
import { SelectCustomValueComponent } from '../../../shared/shared-forms/select/select-custom-value.component'
import { SelectOptionsComponent } from '../../../shared/shared-forms/select/select-options.component'
import { PeerTubeTemplateDirective } from '../../../shared/shared-main/angular/peertube-template.directive'
import { PeerTubeTemplateDirective } from '../../../shared/shared-main/common/peertube-template.directive'
import { ConfigService } from '../shared/config.service'
import { EditConfigurationService, ResolutionOption } from './edit-configuration.service'

View file

@ -1,8 +1,3 @@
<h1>
<my-global-icon iconName="follower" aria-hidden="true"></my-global-icon>
<ng-container i18n>Followers of your instance</ng-container>
</h1>
<p-table
[value]="followers" [paginator]="totalRecords > 0" [totalRecords]="totalRecords" [rows]="rowsPerPage" [first]="pagination.start"
[rowsPerPageOptions]="rowsPerPageOptions" [sortField]="sort.field" [sortOrder]="sort.order"
@ -14,7 +9,7 @@
<div class="caption">
<div class="left-buttons">
<my-action-dropdown
*ngIf="isInSelectionMode()" i18n-label label="Batch actions" theme="orange"
*ngIf="isInSelectionMode()" i18n-label label="Batch actions" theme="primary"
[actions]="bulkActions" [entry]="selectedRows"
>
</my-action-dropdown>

View file

@ -1,22 +1,6 @@
@use '_variables' as *;
@use '_mixins' as *;
a {
display: inline-block;
@include disable-default-a-behaviour;
&,
&:hover {
color: pvar(--mainForegroundColor);
}
span {
font-size: 80%;
color: pvar(--inputPlaceholderColor);
}
}
.action-cell {
my-button:first-child {
@include margin-right(10px);

View file

@ -3,7 +3,7 @@ import { Component, OnInit } from '@angular/core'
import { ConfirmService, Notifier, RestPagination, RestTable } from '@app/core'
import { formatICU } from '@app/helpers'
import { ActorFollow } from '@peertube/peertube-models'
import { AutoColspanDirective } from '../../../shared/shared-main/angular/auto-colspan.directive'
import { AutoColspanDirective } from '../../../shared/shared-main/common/auto-colspan.directive'
import { DeleteButtonComponent } from '../../../shared/shared-main/buttons/delete-button.component'
import { ButtonComponent } from '../../../shared/shared-main/buttons/button.component'
import { NgbTooltip } from '@ng-bootstrap/ng-bootstrap'

View file

@ -26,17 +26,17 @@
</div>
</div>
<div i18n *ngIf="httpEnabled() === false" class="alert alert-warning">
<my-alert i18n *ngIf="httpEnabled() === false" type="warning">
It seems that you are not on a HTTPS server. Your webserver needs to have TLS activated in order to follow servers.
</div>
</my-alert>
<div class="form-group inputs">
<input
type="button" role="button" i18n-value value="Cancel" class="peertube-button grey-button"
type="button" role="button" i18n-value value="Cancel" class="peertube-button secondary-button"
(click)="hide()" (key.enter)="hide()"
>
<input type="submit" i18n-value value="Follow" class="peertube-button orange-button" [disabled]="!form.valid" />
<input type="submit" i18n-value value="Follow" class="peertube-button primary-button" [disabled]="!form.valid" />
</div>
</form>
</div>

View file

@ -7,6 +7,7 @@ import { UNIQUE_HOSTS_OR_HANDLE_VALIDATOR } from '@app/shared/form-validators/ho
import { FormReactive } from '@app/shared/shared-forms/form-reactive'
import { FormReactiveService } from '@app/shared/shared-forms/form-reactive.service'
import { InstanceFollowService } from '@app/shared/shared-instance/instance-follow.service'
import { AlertComponent } from '@app/shared/shared-main/common/alert.component'
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap/modal/modal-ref'
import { splitAndGetNotEmpty } from '@root-helpers/string'
@ -17,7 +18,7 @@ import { GlobalIconComponent } from '../../../shared/shared-icons/global-icon.co
templateUrl: './follow-modal.component.html',
styleUrls: [ './follow-modal.component.scss' ],
standalone: true,
imports: [ GlobalIconComponent, FormsModule, ReactiveFormsModule, NgClass, NgIf ]
imports: [ GlobalIconComponent, FormsModule, ReactiveFormsModule, NgClass, NgIf, AlertComponent ]
})
export class FollowModalComponent extends FormReactive implements OnInit {
@ViewChild('modal', { static: true }) modal: NgbModal

View file

@ -1,8 +1,3 @@
<h1>
<my-global-icon iconName="following" aria-hidden="true"></my-global-icon>
<ng-container i18n>Subscriptions of your instance</ng-container>
</h1>
<p-table
[value]="following" [paginator]="totalRecords > 0" [totalRecords]="totalRecords" [rows]="rowsPerPage" [first]="pagination.start"
[rowsPerPageOptions]="rowsPerPageOptions" [sortField]="sort.field" [sortOrder]="sort.order"
@ -14,7 +9,7 @@
<div class="caption">
<div class="left-buttons">
<my-action-dropdown
*ngIf="isInSelectionMode()" i18n-label label="Batch actions" theme="orange"
*ngIf="isInSelectionMode()" i18n-label label="Batch actions" theme="primary"
[actions]="bulkActions" [entry]="selectedRows"
>
</my-action-dropdown>
@ -25,8 +20,10 @@
</button>
</div>
<div class="ms-auto">
<div class="ms-auto d-flex gap-2 flex-wrap">
<my-advanced-input-filter [filters]="searchFilters" (search)="onSearch($event)"></my-advanced-input-filter>
<my-button i18n-label label="Refresh" icon="refresh" (click)="reloadData()"></my-button>
</div>
</div>
</ng-template>

View file

@ -2,19 +2,7 @@
@use '_mixins' as *;
a {
display: inline-block;
@include disable-default-a-behaviour;
&,
&:hover {
color: pvar(--mainForegroundColor);
}
span {
font-size: 80%;
color: pvar(--inputPlaceholderColor);
}
color: pvar(--fg);
}
my-delete-button {

View file

@ -1,18 +1,19 @@
import { SortMeta, SharedModule } from 'primeng/api'
import { DatePipe, NgIf } from '@angular/common'
import { Component, OnInit, ViewChild } from '@angular/core'
import { ConfirmService, Notifier, RestPagination, RestTable } from '@app/core'
import { ActorFollow } from '@peertube/peertube-models'
import { FollowModalComponent } from './follow-modal.component'
import { formatICU } from '@app/helpers'
import { AutoColspanDirective } from '../../../shared/shared-main/angular/auto-colspan.directive'
import { RedundancyCheckboxComponent } from '../shared/redundancy-checkbox.component'
import { DeleteButtonComponent } from '../../../shared/shared-main/buttons/delete-button.component'
import { AdvancedInputFilter, AdvancedInputFilterComponent } from '../../../shared/shared-forms/advanced-input-filter.component'
import { ActionDropdownComponent, DropdownAction } from '../../../shared/shared-main/buttons/action-dropdown.component'
import { NgIf, DatePipe } from '@angular/common'
import { TableModule } from 'primeng/table'
import { GlobalIconComponent } from '../../../shared/shared-icons/global-icon.component'
import { InstanceFollowService } from '@app/shared/shared-instance/instance-follow.service'
import { ActorFollow } from '@peertube/peertube-models'
import { SharedModule, SortMeta } from 'primeng/api'
import { TableModule } from 'primeng/table'
import { AdvancedInputFilter, AdvancedInputFilterComponent } from '../../../shared/shared-forms/advanced-input-filter.component'
import { GlobalIconComponent } from '../../../shared/shared-icons/global-icon.component'
import { ActionDropdownComponent, DropdownAction } from '../../../shared/shared-main/buttons/action-dropdown.component'
import { ButtonComponent } from '../../../shared/shared-main/buttons/button.component'
import { DeleteButtonComponent } from '../../../shared/shared-main/buttons/delete-button.component'
import { AutoColspanDirective } from '../../../shared/shared-main/common/auto-colspan.directive'
import { RedundancyCheckboxComponent } from '../shared/redundancy-checkbox.component'
import { FollowModalComponent } from './follow-modal.component'
@Component({
templateUrl: './following-list.component.html',
@ -29,7 +30,8 @@ import { InstanceFollowService } from '@app/shared/shared-instance/instance-foll
RedundancyCheckboxComponent,
AutoColspanDirective,
FollowModalComponent,
DatePipe
DatePipe,
ButtonComponent
]
})
export class FollowingListComponent extends RestTable <ActorFollow> implements OnInit {

View file

@ -5,7 +5,7 @@ import { UserRight } from '@peertube/peertube-models'
import { FollowersListComponent } from './followers-list'
import { FollowingListComponent } from './following-list/following-list.component'
export const FollowsRoutes: Routes = [
export const followsRoutes: Routes = [
{
path: 'follows',
canActivate: [ UserRightGuard ],

View file

@ -1,8 +1,3 @@
<h1>
<my-global-icon iconName="videos" aria-hidden="true"></my-global-icon>
<ng-container i18n>Videos redundancies</ng-container>
</h1>
<div class="admin-sub-header">
<div class="select-filter-block">
<label for="displayType" i18n>Display</label>
@ -98,7 +93,7 @@
</div>
<div class="chart-block" *ngFor="let r of redundanciesGraphsData">
<p-chart type="pie" [data]="r.graphData" [options]="r.options" width="300px" height="300px"></p-chart>
<p-chart [ariaLabel]="r.ariaLabel" type="pie" [data]="r.graphData" [options]="r.options" width="300px" height="300px"></p-chart>
</div>
</div>

View file

@ -1,20 +1,9 @@
@use '_variables' as *;
@use '_mixins' as *;
@use '_form-mixins' as *;
a {
display: inline-block;
@include disable-default-a-behaviour;
&,
&:hover {
color: pvar(--mainForegroundColor);
}
span {
font-size: 80%;
color: pvar(--inputPlaceholderColor);
}
color: pvar(--fg);
}
.expansion-block {
@ -24,18 +13,8 @@ a {
.admin-sub-header {
justify-content: flex-end;
.select-filter-block {
&:not(:last-child) {
@include margin-right(10px);
}
label {
margin-bottom: 2px;
}
.peertube-select-container {
@include peertube-select-container(auto);
}
.peertube-select-container {
@include peertube-select-container(auto);
}
}

View file

@ -1,21 +1,21 @@
import { ChartData, ChartOptions, TooltipItem } from 'chart.js'
import { SortMeta, SharedModule } from 'primeng/api'
import { NgFor, NgIf } from '@angular/common'
import { Component, OnInit } from '@angular/core'
import { FormsModule } from '@angular/forms'
import { ConfirmService, Notifier, RestPagination, RestTable, ServerService } from '@app/core'
import { BytesPipe } from '@app/shared/shared-main/common/bytes.pipe'
import { RedundancyService } from '@app/shared/shared-main/video/redundancy.service'
import { NgbTooltip } from '@ng-bootstrap/ng-bootstrap'
import { VideoRedundanciesTarget, VideoRedundancy, VideosRedundancyStats } from '@peertube/peertube-models'
import { peertubeLocalStorage } from '@root-helpers/peertube-web-storage'
import { ChartData, ChartOptions, TooltipItem } from 'chart.js'
import { SharedModule, SortMeta } from 'primeng/api'
import { ChartModule } from 'primeng/chart'
import { VideoRedundancyInformationComponent } from './video-redundancy-information.component'
import { AutoColspanDirective } from '../../../shared/shared-main/angular/auto-colspan.directive'
import { DeleteButtonComponent } from '../../../shared/shared-main/buttons/delete-button.component'
import { TableExpanderIconComponent } from '../../../shared/shared-tables/table-expander-icon.component'
import { NgbTooltip } from '@ng-bootstrap/ng-bootstrap'
import { NgIf, NgFor } from '@angular/common'
import { TableModule } from 'primeng/table'
import { FormsModule } from '@angular/forms'
import { GlobalIconComponent } from '../../../shared/shared-icons/global-icon.component'
import { BytesPipe } from '@app/shared/shared-main/angular/bytes.pipe'
import { RedundancyService } from '@app/shared/shared-main/video/redundancy.service'
import { DeleteButtonComponent } from '../../../shared/shared-main/buttons/delete-button.component'
import { AutoColspanDirective } from '../../../shared/shared-main/common/auto-colspan.directive'
import { TableExpanderIconComponent } from '../../../shared/shared-tables/table-expander-icon.component'
import { VideoRedundancyInformationComponent } from './video-redundancy-information.component'
@Component({
selector: 'my-video-redundancies-list',
@ -39,7 +39,7 @@ import { RedundancyService } from '@app/shared/shared-main/video/redundancy.serv
]
})
export class VideoRedundanciesListComponent extends RestTable implements OnInit {
private static LOCAL_STORAGE_DISPLAY_TYPE = 'video-redundancies-list-display-type'
private static LS_DISPLAY_TYPE = 'video-redundancies-list-display-type'
videoRedundancies: VideoRedundancy[] = []
totalRecords = 0
@ -48,7 +48,7 @@ export class VideoRedundanciesListComponent extends RestTable implements OnInit
pagination: RestPagination = { count: this.rowsPerPage, start: 0 }
displayType: VideoRedundanciesTarget = 'my-videos'
redundanciesGraphsData: { stats: VideosRedundancyStats, graphData: ChartData, options: ChartOptions }[] = []
redundanciesGraphsData: { stats: VideosRedundancyStats, graphData: ChartData, options: ChartOptions, ariaLabel: string }[] = []
noRedundancies = false
@ -133,6 +133,8 @@ export class VideoRedundanciesListComponent extends RestTable implements OnInit
this.redundanciesGraphsData.push({
stats,
ariaLabel: $localize`Redundancy strategy "${stats.strategy}". ` + labels.join('. '),
graphData: {
labels,
datasets: [
@ -149,6 +151,7 @@ export class VideoRedundanciesListComponent extends RestTable implements OnInit
}
]
},
options: {
plugins: {
title: {
@ -208,12 +211,12 @@ export class VideoRedundanciesListComponent extends RestTable implements OnInit
}
private loadSelectLocalStorage () {
const displayType = peertubeLocalStorage.getItem(VideoRedundanciesListComponent.LOCAL_STORAGE_DISPLAY_TYPE)
const displayType = peertubeLocalStorage.getItem(VideoRedundanciesListComponent.LS_DISPLAY_TYPE)
if (displayType) this.displayType = displayType as VideoRedundanciesTarget
}
private saveSelectLocalStorage () {
peertubeLocalStorage.setItem(VideoRedundanciesListComponent.LOCAL_STORAGE_DISPLAY_TYPE, this.displayType)
peertubeLocalStorage.setItem(VideoRedundanciesListComponent.LS_DISPLAY_TYPE, this.displayType)
}
private bytesToHuman (bytes: number) {

View file

@ -1,6 +1,6 @@
import { Component, Input } from '@angular/core'
import { FileRedundancyInformation, StreamingPlaylistRedundancyInformation } from '@peertube/peertube-models'
import { BytesPipe } from '../../../shared/shared-main/angular/bytes.pipe'
import { BytesPipe } from '../../../shared/shared-main/common/bytes.pipe'
import { DatePipe } from '@angular/common'
@Component({

View file

@ -1,6 +1 @@
<h1>
<my-global-icon iconName="flag" aria-hidden="true"></my-global-icon>
<ng-container i18n>Reports</ng-container>
</h1>
<my-abuse-list-table viewType="admin"></my-abuse-list-table>

View file

@ -1,6 +1,6 @@
import { Component } from '@angular/core'
import { NgIf, DatePipe } from '@angular/common'
import { AutoColspanDirective } from '../../../shared/shared-main/angular/auto-colspan.directive'
import { AutoColspanDirective } from '../../../shared/shared-main/common/auto-colspan.directive'
import { ActorAvatarComponent } from '../../../shared/shared-actor-image/actor-avatar.component'
import { NgbTooltip } from '@ng-bootstrap/ng-bootstrap'
import { AdvancedInputFilterComponent } from '../../../shared/shared-forms/advanced-input-filter.component'

View file

@ -1,7 +1,7 @@
import { Component } from '@angular/core'
import { BatchDomainsModalComponent } from '../../../shared/shared-moderation/batch-domains-modal.component'
import { NgIf, DatePipe } from '@angular/common'
import { AutoColspanDirective } from '../../../shared/shared-main/angular/auto-colspan.directive'
import { AutoColspanDirective } from '../../../shared/shared-main/common/auto-colspan.directive'
import { NgbTooltip } from '@ng-bootstrap/ng-bootstrap'
import { AdvancedInputFilterComponent } from '../../../shared/shared-forms/advanced-input-filter.component'
import { SharedModule } from 'primeng/api'

View file

@ -7,7 +7,7 @@ import { UserRight } from '@peertube/peertube-models'
import { RegistrationListComponent } from './registration-list'
import { WatchedWordsListAdminComponent } from './watched-words-list/watched-words-list-admin.component'
export const ModerationRoutes: Routes = [
export const moderationRoutes: Routes = [
{
path: 'moderation',
children: [
@ -90,7 +90,7 @@ export const ModerationRoutes: Routes = [
},
{
path: 'video-comments/list',
redirectTo: '/admin/comments/list',
redirectTo: '/admin/overview/comments/list',
pathMatch: 'full'
},

View file

@ -13,9 +13,9 @@
<form novalidate [formGroup]="form" (ngSubmit)="processRegistration()">
<div class="modal-body mb-3">
<div i18n *ngIf="!registration.emailVerified" class="alert alert-warning">
<my-alert i18n *ngIf="!registration.emailVerified" type="warning">
Registration email has not been verified. Email delivery has been disabled by default.
</div>
</my-alert>
<div class="description">
<ng-container *ngIf="isAccept()">
@ -27,9 +27,9 @@
An email will be sent to <em>{{ registration.email }}</em> explaining its account has been created with the moderation response you'll write below.
</p>
<div *ngIf="!isEmailEnabled()" class="alert alert-warning" i18n>
<my-alert *ngIf="!isEmailEnabled()" type="warning" i18n>
Emails are not enabled on this instance so PeerTube won't be able to send an email to <em>{{ registration.email }}</em> explaining its account has been created.
</div>
</my-alert>
</ng-container>
<ng-container *ngIf="isReject()">
@ -37,9 +37,9 @@
An email will be sent to <em>{{ registration.email }}</em> explaining its registration request has been <strong>rejected</strong> with the moderation response you'll write below.
</p>
<div *ngIf="!isEmailEnabled()" class="alert alert-warning" i18n>
<my-alert *ngIf="!isEmailEnabled()" type="warning" i18n>
Emails are not enabled on this instance so PeerTube won't be able to send an email to <em>{{ registration.email }}</em> explaining its registration request has been rejected.
</div>
</my-alert>
</ng-container>
</div>
@ -66,11 +66,11 @@
<div class="modal-footer inputs">
<input
type="button" role="button" i18n-value value="Cancel" class="peertube-button grey-button"
type="button" role="button" i18n-value value="Cancel" class="peertube-button secondary-button"
(click)="hide()" (key.enter)="hide()"
>
<input type="submit" [value]="getSubmitValue()" class="peertube-button orange-button" [disabled]="!form.valid">
<input type="submit" [value]="getSubmitValue()" class="peertube-button primary-button" [disabled]="!form.valid">
</div>
</form>
</ng-template>

View file

@ -1,22 +1,23 @@
import { NgClass, NgIf } from '@angular/common'
import { Component, EventEmitter, OnInit, Output, ViewChild } from '@angular/core'
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
import { Notifier, ServerService } from '@app/core'
import { FormReactive } from '@app/shared/shared-forms/form-reactive'
import { FormReactiveService } from '@app/shared/shared-forms/form-reactive.service'
import { AlertComponent } from '@app/shared/shared-main/common/alert.component'
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap/modal/modal-ref'
import { UserRegistration } from '@peertube/peertube-models'
import { PeertubeCheckboxComponent } from '../../../shared/shared-forms/peertube-checkbox.component'
import { GlobalIconComponent } from '../../../shared/shared-icons/global-icon.component'
import { AdminRegistrationService } from './admin-registration.service'
import { REGISTRATION_MODERATION_RESPONSE_VALIDATOR } from './process-registration-validators'
import { PeertubeCheckboxComponent } from '../../../shared/shared-forms/peertube-checkbox.component'
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
import { GlobalIconComponent } from '../../../shared/shared-icons/global-icon.component'
import { NgIf, NgClass } from '@angular/common'
@Component({
selector: 'my-process-registration-modal',
templateUrl: './process-registration-modal.component.html',
standalone: true,
imports: [ NgIf, GlobalIconComponent, FormsModule, ReactiveFormsModule, NgClass, PeertubeCheckboxComponent ]
imports: [ NgIf, GlobalIconComponent, FormsModule, ReactiveFormsModule, NgClass, PeertubeCheckboxComponent, AlertComponent ]
})
export class ProcessRegistrationModalComponent extends FormReactive implements OnInit {
@ViewChild('modal', { static: true }) modal: NgbModal

View file

@ -1,8 +1,3 @@
<h1>
<my-global-icon iconName="user" aria-hidden="true"></my-global-icon>
<ng-container i18n>Registration requests</ng-container>
</h1>
<p-table
[value]="registrations" [paginator]="totalRecords > 0" [totalRecords]="totalRecords" [rows]="rowsPerPage" [first]="pagination.start"
[rowsPerPageOptions]="rowsPerPageOptions" [sortField]="sort.field" [sortOrder]="sort.order" dataKey="id"
@ -14,7 +9,7 @@
<div class="caption">
<div class="left-buttons">
<my-action-dropdown
*ngIf="isInSelectionMode()" i18n-label label="Batch actions" theme="orange"
*ngIf="isInSelectionMode()" i18n-label label="Batch actions" theme="primary"
[actions]="bulkActions" [entry]="selectedRows"
>
</my-action-dropdown>
@ -60,7 +55,7 @@
<td class="action-cell">
<my-action-dropdown
[ngClass]="{ 'show': expanded }" placement="bottom-right top-right left auto" container="body"
i18n-label label="Actions" [actions]="registrationActions" [entry]="registration"
i18n-label label="Actions" [actions]="registrationActions" [entry]="registration" buttonSize="small"
></my-action-dropdown>
</td>

View file

@ -6,7 +6,7 @@ import { formatICU } from '@app/helpers'
import { UserRegistration, UserRegistrationState } from '@peertube/peertube-models'
import { AdminRegistrationService } from './admin-registration.service'
import { ProcessRegistrationModalComponent } from './process-registration-modal.component'
import { AutoColspanDirective } from '../../../shared/shared-main/angular/auto-colspan.directive'
import { AutoColspanDirective } from '../../../shared/shared-main/common/auto-colspan.directive'
import { UserEmailInfoComponent } from '../../shared/user-email-info.component'
import { TableExpanderIconComponent } from '../../../shared/shared-tables/table-expander-icon.component'
import { NgbTooltip } from '@ng-bootstrap/ng-bootstrap'

View file

@ -1,8 +1,3 @@
<h1>
<my-global-icon iconName="cross" aria-hidden="true"></my-global-icon>
<ng-container i18n>Video blocks</ng-container>
</h1>
<p-table
[value]="blocklist" [paginator]="totalRecords > 0" [totalRecords]="totalRecords" [rows]="rowsPerPage" [first]="pagination.start"
[rowsPerPageOptions]="rowsPerPageOptions" [sortField]="sort.field" [sortOrder]="sort.order" dataKey="id"
@ -46,7 +41,7 @@
<td class="action-cell">
<my-action-dropdown
[ngClass]="{ 'show': expanded }" placement="bottom-right auto" container="body"
i18n-label label="Actions" [actions]="videoBlocklistActions" [entry]="videoBlock"
i18n-label label="Actions" [actions]="videoBlocklistActions" [entry]="videoBlock" buttonSize="small"
></my-action-dropdown>
</td>

View file

@ -8,7 +8,7 @@ import { buildVideoEmbedLink, decorateVideoLink } from '@peertube/peertube-core-
import { VideoBlacklist, VideoBlacklistType, VideoBlacklistType_Type } from '@peertube/peertube-models'
import { buildVideoOrPlaylistEmbed } from '@root-helpers/video'
import { EmbedComponent } from '../../../shared/shared-main/video/embed.component'
import { AutoColspanDirective } from '../../../shared/shared-main/angular/auto-colspan.directive'
import { AutoColspanDirective } from '../../../shared/shared-main/common/auto-colspan.directive'
import { VideoCellComponent } from '../../../shared/shared-tables/video-cell.component'
import { ActionDropdownComponent, DropdownAction } from '../../../shared/shared-main/buttons/action-dropdown.component'
import { TableExpanderIconComponent } from '../../../shared/shared-tables/table-expander-icon.component'
@ -23,7 +23,7 @@ import { VideoBlockService } from '@app/shared/shared-moderation/video-block.ser
@Component({
selector: 'my-video-block-list',
templateUrl: './video-block-list.component.html',
styleUrls: [ '../../../shared/shared-moderation/moderation.scss', './video-block-list.component.scss' ],
styleUrls: [ '../../../shared/shared-moderation/moderation.scss' ],
standalone: true,
imports: [
GlobalIconComponent,

View file

@ -1,8 +1,3 @@
<h1>
<my-global-icon iconName="eye-open" aria-hidden="true"></my-global-icon>
<ng-container i18n>Instance watched words lists</ng-container>
</h1>
<em class="d-block" i18n>Video name/description and comments that contain any of the watched words are automatically tagged with the name of the list.</em>
<em class="d-block mb-3" i18n>These automatic tags can be used to filter comments and videos.</em>

View file

@ -1,2 +1 @@
export * from './video-comment-list.component'
export * from './video-comment.routes'

View file

@ -1,10 +1,3 @@
<h1>
<my-global-icon iconName="message-circle" aria-hidden="true"></my-global-icon>
<ng-container i18n>Video comments</ng-container>
<my-feed [syndicationItems]="syndicationItems"></my-feed>
</h1>
<em i18n>This view also shows comments from muted accounts.</em>
<my-video-comment-list-admin-owner mode="admin"></my-video-comment-list-admin-owner>

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